@vocoder/cli 0.1.16 → 0.1.18

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.
@@ -0,0 +1,267 @@
1
+ // src/utils/detect-local.ts
2
+ import { existsSync, readFileSync } from "fs";
3
+ import { join } from "path";
4
+ function detectLocalEcosystem(cwd = process.cwd()) {
5
+ const packageManager = detectPackageManager(cwd);
6
+ const pkg = readPackageJson(cwd);
7
+ if (!pkg) {
8
+ return {
9
+ ecosystem: null,
10
+ framework: null,
11
+ packageManager,
12
+ uiPackage: null,
13
+ hasUnplugin: false,
14
+ hasUiPackage: false,
15
+ sourceLocale: null
16
+ };
17
+ }
18
+ const allDeps = {
19
+ ...pkg.dependencies ?? {},
20
+ ...pkg.devDependencies ?? {}
21
+ };
22
+ const hasUnplugin = "@vocoder/unplugin" in allDeps;
23
+ const { ecosystem, framework, uiPackage } = detectFromDeps(allDeps, cwd);
24
+ const hasUiPackage = uiPackage !== null && uiPackage in allDeps;
25
+ return {
26
+ ecosystem,
27
+ framework,
28
+ packageManager,
29
+ uiPackage,
30
+ hasUnplugin,
31
+ hasUiPackage,
32
+ sourceLocale: null
33
+ };
34
+ }
35
+ function detectPackageManager(cwd) {
36
+ if (existsSync(join(cwd, "pnpm-lock.yaml"))) return "pnpm";
37
+ if (existsSync(join(cwd, "bun.lockb")) || existsSync(join(cwd, "bun.lock")))
38
+ return "bun";
39
+ if (existsSync(join(cwd, "yarn.lock"))) return "yarn";
40
+ return "npm";
41
+ }
42
+ function readPackageJson(cwd) {
43
+ const pkgPath = join(cwd, "package.json");
44
+ if (!existsSync(pkgPath)) return null;
45
+ try {
46
+ return JSON.parse(readFileSync(pkgPath, "utf-8"));
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+ function detectFromDeps(allDeps, cwd) {
52
+ if ("vue" in allDeps) {
53
+ const framework = "nuxt" in allDeps ? "nuxt" : null;
54
+ return { ecosystem: "vue", framework, uiPackage: "@vocoder/vue" };
55
+ }
56
+ if ("svelte" in allDeps) {
57
+ const framework = "@sveltejs/kit" in allDeps ? "sveltekit" : null;
58
+ return { ecosystem: "svelte", framework, uiPackage: "@vocoder/svelte" };
59
+ }
60
+ if ("@angular/core" in allDeps || existsSync(join(cwd, "angular.json"))) {
61
+ return {
62
+ ecosystem: "angular",
63
+ framework: "angular",
64
+ uiPackage: "@vocoder/angular"
65
+ };
66
+ }
67
+ if ("react" in allDeps) {
68
+ let framework = null;
69
+ if ("next" in allDeps) framework = "nextjs";
70
+ else if ("@remix-run/react" in allDeps) framework = "remix";
71
+ else if ("gatsby" in allDeps) framework = "gatsby";
72
+ else if ("vite" in allDeps) framework = "vite";
73
+ return { ecosystem: "react", framework, uiPackage: "@vocoder/react" };
74
+ }
75
+ return { ecosystem: null, framework: null, uiPackage: null };
76
+ }
77
+ function buildInstallCommand(packageManager, packages) {
78
+ if (packages.length === 0) return "";
79
+ const pkgList = packages.join(" ");
80
+ switch (packageManager) {
81
+ case "pnpm":
82
+ return `pnpm add ${pkgList}`;
83
+ case "yarn":
84
+ return `yarn add ${pkgList}`;
85
+ case "bun":
86
+ return `bun add ${pkgList}`;
87
+ default:
88
+ return `npm install ${pkgList}`;
89
+ }
90
+ }
91
+ function getPackagesToInstall(detection) {
92
+ const packages = [];
93
+ if (!detection.hasUnplugin) packages.push("@vocoder/unplugin");
94
+ if (detection.uiPackage && !detection.hasUiPackage)
95
+ packages.push(detection.uiPackage);
96
+ return packages;
97
+ }
98
+
99
+ // src/utils/setup-snippets.ts
100
+ function getSetupSnippets(params) {
101
+ const { framework, ecosystem, sourceLocale } = params;
102
+ return {
103
+ pluginStep: getPluginSnippet(framework, ecosystem),
104
+ providerStep: getProviderSnippet(ecosystem, sourceLocale),
105
+ wrapStep: getWrapSnippet(ecosystem),
106
+ whatsNext: "Push to a target branch to trigger translations."
107
+ };
108
+ }
109
+ function getPluginSnippet(framework, ecosystem) {
110
+ switch (framework) {
111
+ case "nextjs":
112
+ return {
113
+ file: "next.config.ts",
114
+ code: `import { withVocoder } from '@vocoder/unplugin/next';
115
+
116
+ export default withVocoder({
117
+ // your existing Next.js config
118
+ });`
119
+ };
120
+ case "vite":
121
+ case "remix":
122
+ return {
123
+ file: "vite.config.ts",
124
+ code: `import vocoder from '@vocoder/unplugin/vite';
125
+
126
+ export default defineConfig({
127
+ plugins: [
128
+ vocoder(),
129
+ // your other plugins
130
+ ],
131
+ });`
132
+ };
133
+ case "nuxt":
134
+ return {
135
+ file: "nuxt.config.ts",
136
+ code: `import vocoder from '@vocoder/unplugin/vite';
137
+
138
+ export default defineNuxtConfig({
139
+ vite: {
140
+ plugins: [vocoder()],
141
+ },
142
+ });`
143
+ };
144
+ case "sveltekit":
145
+ return {
146
+ file: "vite.config.ts",
147
+ code: `import vocoder from '@vocoder/unplugin/vite';
148
+ import { sveltekit } from '@sveltejs/kit/vite';
149
+
150
+ export default defineConfig({
151
+ plugins: [
152
+ sveltekit(),
153
+ vocoder(),
154
+ ],
155
+ });`
156
+ };
157
+ case "gatsby":
158
+ return {
159
+ file: "gatsby-node.js",
160
+ code: `const vocoder = require('@vocoder/unplugin/webpack');
161
+
162
+ exports.onCreateWebpackConfig = ({ actions }) => {
163
+ actions.setWebpackConfig({
164
+ plugins: [vocoder()],
165
+ });
166
+ };`
167
+ };
168
+ case "angular":
169
+ return null;
170
+ // Angular CLI doesn't expose plugin config easily
171
+ default:
172
+ if (ecosystem) {
173
+ return {
174
+ file: "your bundler config",
175
+ code: `// Vite
176
+ import vocoder from '@vocoder/unplugin/vite';
177
+ // Webpack
178
+ const vocoder = require('@vocoder/unplugin/webpack');
179
+
180
+ // Add vocoder() to your plugins array`
181
+ };
182
+ }
183
+ return null;
184
+ }
185
+ }
186
+ function getProviderSnippet(ecosystem, sourceLocale) {
187
+ switch (ecosystem) {
188
+ case "react":
189
+ return {
190
+ file: "your root layout or App component",
191
+ code: `import { VocoderProvider } from '@vocoder/react';
192
+
193
+ <VocoderProvider defaultLocale="${sourceLocale}">
194
+ {children}
195
+ </VocoderProvider>`
196
+ };
197
+ case "vue":
198
+ return {
199
+ file: "your app entry",
200
+ code: `import { createVocoder } from '@vocoder/vue';
201
+
202
+ const vocoder = createVocoder({
203
+ defaultLocale: '${sourceLocale}',
204
+ });
205
+
206
+ app.use(vocoder);`
207
+ };
208
+ case "svelte":
209
+ return {
210
+ file: "your root layout",
211
+ code: `<script>
212
+ import { VocoderProvider } from '@vocoder/svelte';
213
+ </script>
214
+
215
+ <VocoderProvider defaultLocale="${sourceLocale}">
216
+ <slot />
217
+ </VocoderProvider>`
218
+ };
219
+ default:
220
+ return null;
221
+ }
222
+ }
223
+ function getWrapSnippet(ecosystem) {
224
+ switch (ecosystem) {
225
+ case "react":
226
+ return {
227
+ code: `import { T } from '@vocoder/react';
228
+
229
+ <T>Hello, world!</T>`
230
+ };
231
+ case "vue":
232
+ return {
233
+ code: `<template>
234
+ <T>Hello, world!</T>
235
+ </template>
236
+
237
+ <script setup>
238
+ import { T } from '@vocoder/vue';
239
+ </script>`
240
+ };
241
+ case "svelte":
242
+ return {
243
+ code: `<script>
244
+ import { T } from '@vocoder/svelte';
245
+ </script>
246
+
247
+ <T>Hello, world!</T>`
248
+ };
249
+ default:
250
+ return {
251
+ code: `// Wrap translatable strings with <T>
252
+ <T>Hello, world!</T>`
253
+ };
254
+ }
255
+ }
256
+
257
+ // src/utils/extract.ts
258
+ import { StringExtractor } from "@vocoder/extractor";
259
+
260
+ export {
261
+ detectLocalEcosystem,
262
+ buildInstallCommand,
263
+ getPackagesToInstall,
264
+ getSetupSnippets,
265
+ StringExtractor
266
+ };
267
+ //# sourceMappingURL=chunk-OFQLREXF.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/detect-local.ts","../src/utils/setup-snippets.ts","../src/utils/extract.ts"],"sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nexport type PackageManager = \"pnpm\" | \"npm\" | \"yarn\" | \"bun\";\n\nexport type DetectedFramework =\n\t| \"nextjs\"\n\t| \"vite\"\n\t| \"remix\"\n\t| \"nuxt\"\n\t| \"sveltekit\"\n\t| \"gatsby\"\n\t| \"angular\"\n\t| null;\n\nexport type DetectedEcosystem = \"react\" | \"vue\" | \"svelte\" | \"angular\" | null;\n\nexport interface LocalDetectionResult {\n\tecosystem: DetectedEcosystem;\n\tframework: DetectedFramework;\n\tpackageManager: PackageManager;\n\tuiPackage: string | null;\n\thasUnplugin: boolean;\n\thasUiPackage: boolean;\n\tsourceLocale: string | null;\n}\n\n/**\n * Detect the local project's ecosystem, framework, and package manager\n * by inspecting filesystem artifacts. No network calls.\n */\nexport function detectLocalEcosystem(\n\tcwd: string = process.cwd(),\n): LocalDetectionResult {\n\tconst packageManager = detectPackageManager(cwd);\n\tconst pkg = readPackageJson(cwd);\n\n\tif (!pkg) {\n\t\treturn {\n\t\t\tecosystem: null,\n\t\t\tframework: null,\n\t\t\tpackageManager,\n\t\t\tuiPackage: null,\n\t\t\thasUnplugin: false,\n\t\t\thasUiPackage: false,\n\t\t\tsourceLocale: null,\n\t\t};\n\t}\n\n\tconst allDeps = {\n\t\t...((pkg.dependencies as Record<string, string>) ?? {}),\n\t\t...((pkg.devDependencies as Record<string, string>) ?? {}),\n\t};\n\n\tconst hasUnplugin = \"@vocoder/unplugin\" in allDeps;\n\n\t// Detect ecosystem + framework\n\tconst { ecosystem, framework, uiPackage } = detectFromDeps(allDeps, cwd);\n\tconst hasUiPackage = uiPackage !== null && uiPackage in allDeps;\n\n\treturn {\n\t\tecosystem,\n\t\tframework,\n\t\tpackageManager,\n\t\tuiPackage,\n\t\thasUnplugin,\n\t\thasUiPackage,\n\t\tsourceLocale: null,\n\t};\n}\n\nfunction detectPackageManager(cwd: string): PackageManager {\n\tif (existsSync(join(cwd, \"pnpm-lock.yaml\"))) return \"pnpm\";\n\tif (existsSync(join(cwd, \"bun.lockb\")) || existsSync(join(cwd, \"bun.lock\")))\n\t\treturn \"bun\";\n\tif (existsSync(join(cwd, \"yarn.lock\"))) return \"yarn\";\n\treturn \"npm\";\n}\n\nfunction readPackageJson(cwd: string): Record<string, unknown> | null {\n\tconst pkgPath = join(cwd, \"package.json\");\n\tif (!existsSync(pkgPath)) return null;\n\ttry {\n\t\treturn JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction detectFromDeps(\n\tallDeps: Record<string, string>,\n\tcwd: string,\n): {\n\tecosystem: DetectedEcosystem;\n\tframework: DetectedFramework;\n\tuiPackage: string | null;\n} {\n\t// Vue ecosystem\n\tif (\"vue\" in allDeps) {\n\t\tconst framework = \"nuxt\" in allDeps ? (\"nuxt\" as const) : null;\n\t\treturn { ecosystem: \"vue\", framework, uiPackage: \"@vocoder/vue\" };\n\t}\n\n\t// Svelte ecosystem\n\tif (\"svelte\" in allDeps) {\n\t\tconst framework =\n\t\t\t\"@sveltejs/kit\" in allDeps ? (\"sveltekit\" as const) : null;\n\t\treturn { ecosystem: \"svelte\", framework, uiPackage: \"@vocoder/svelte\" };\n\t}\n\n\t// Angular ecosystem\n\tif (\"@angular/core\" in allDeps || existsSync(join(cwd, \"angular.json\"))) {\n\t\treturn {\n\t\t\tecosystem: \"angular\",\n\t\t\tframework: \"angular\",\n\t\t\tuiPackage: \"@vocoder/angular\",\n\t\t};\n\t}\n\n\t// React ecosystem (most common — check last)\n\tif (\"react\" in allDeps) {\n\t\tlet framework: DetectedFramework = null;\n\t\tif (\"next\" in allDeps) framework = \"nextjs\";\n\t\telse if (\"@remix-run/react\" in allDeps) framework = \"remix\";\n\t\telse if (\"gatsby\" in allDeps) framework = \"gatsby\";\n\t\telse if (\"vite\" in allDeps) framework = \"vite\";\n\t\treturn { ecosystem: \"react\", framework, uiPackage: \"@vocoder/react\" };\n\t}\n\n\treturn { ecosystem: null, framework: null, uiPackage: null };\n}\n\n/**\n * Build the install command for packages that aren't already installed.\n */\nexport function buildInstallCommand(\n\tpackageManager: PackageManager,\n\tpackages: string[],\n): string {\n\tif (packages.length === 0) return \"\";\n\tconst pkgList = packages.join(\" \");\n\tswitch (packageManager) {\n\t\tcase \"pnpm\":\n\t\t\treturn `pnpm add ${pkgList}`;\n\t\tcase \"yarn\":\n\t\t\treturn `yarn add ${pkgList}`;\n\t\tcase \"bun\":\n\t\t\treturn `bun add ${pkgList}`;\n\t\tdefault:\n\t\t\treturn `npm install ${pkgList}`;\n\t}\n}\n\n/**\n * Get the list of packages that need to be installed.\n */\nexport function getPackagesToInstall(\n\tdetection: LocalDetectionResult,\n): string[] {\n\tconst packages: string[] = [];\n\tif (!detection.hasUnplugin) packages.push(\"@vocoder/unplugin\");\n\tif (detection.uiPackage && !detection.hasUiPackage)\n\t\tpackages.push(detection.uiPackage);\n\treturn packages;\n}\n","import type { DetectedEcosystem, DetectedFramework } from \"./detect-local.js\";\n\nexport interface SetupSnippets {\n\tpluginStep: { file: string; code: string } | null;\n\tproviderStep: { file: string; code: string } | null;\n\twrapStep: { code: string };\n\twhatsNext: string;\n}\n\n/**\n * Generate framework-specific setup snippets.\n */\nexport function getSetupSnippets(params: {\n\tframework: DetectedFramework;\n\tecosystem: DetectedEcosystem;\n\tsourceLocale: string;\n\ttargetBranches: string[];\n}): SetupSnippets {\n\tconst { framework, ecosystem, sourceLocale } = params;\n\n\treturn {\n\t\tpluginStep: getPluginSnippet(framework, ecosystem),\n\t\tproviderStep: getProviderSnippet(ecosystem, sourceLocale),\n\t\twrapStep: getWrapSnippet(ecosystem),\n\t\twhatsNext: \"Push to a target branch to trigger translations.\",\n\t};\n}\n\nfunction getPluginSnippet(\n\tframework: DetectedFramework,\n\tecosystem: DetectedEcosystem,\n): { file: string; code: string } | null {\n\tswitch (framework) {\n\t\tcase \"nextjs\":\n\t\t\treturn {\n\t\t\t\tfile: \"next.config.ts\",\n\t\t\t\tcode: `import { withVocoder } from '@vocoder/unplugin/next';\n\nexport default withVocoder({\n // your existing Next.js config\n});`,\n\t\t\t};\n\n\t\tcase \"vite\":\n\t\tcase \"remix\":\n\t\t\treturn {\n\t\t\t\tfile: \"vite.config.ts\",\n\t\t\t\tcode: `import vocoder from '@vocoder/unplugin/vite';\n\nexport default defineConfig({\n plugins: [\n vocoder(),\n // your other plugins\n ],\n});`,\n\t\t\t};\n\n\t\tcase \"nuxt\":\n\t\t\treturn {\n\t\t\t\tfile: \"nuxt.config.ts\",\n\t\t\t\tcode: `import vocoder from '@vocoder/unplugin/vite';\n\nexport default defineNuxtConfig({\n vite: {\n plugins: [vocoder()],\n },\n});`,\n\t\t\t};\n\n\t\tcase \"sveltekit\":\n\t\t\treturn {\n\t\t\t\tfile: \"vite.config.ts\",\n\t\t\t\tcode: `import vocoder from '@vocoder/unplugin/vite';\nimport { sveltekit } from '@sveltejs/kit/vite';\n\nexport default defineConfig({\n plugins: [\n sveltekit(),\n vocoder(),\n ],\n});`,\n\t\t\t};\n\n\t\tcase \"gatsby\":\n\t\t\treturn {\n\t\t\t\tfile: \"gatsby-node.js\",\n\t\t\t\tcode: `const vocoder = require('@vocoder/unplugin/webpack');\n\nexports.onCreateWebpackConfig = ({ actions }) => {\n actions.setWebpackConfig({\n plugins: [vocoder()],\n });\n};`,\n\t\t\t};\n\n\t\tcase \"angular\":\n\t\t\treturn null; // Angular CLI doesn't expose plugin config easily\n\n\t\tdefault:\n\t\t\t// No known framework — if they have React/Vue/Svelte, they likely have a bundler\n\t\t\t// but we can't guess which config file. Give generic advice.\n\t\t\tif (ecosystem) {\n\t\t\t\treturn {\n\t\t\t\t\tfile: \"your bundler config\",\n\t\t\t\t\tcode: `// Vite\nimport vocoder from '@vocoder/unplugin/vite';\n// Webpack\nconst vocoder = require('@vocoder/unplugin/webpack');\n\n// Add vocoder() to your plugins array`,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn null;\n\t}\n}\n\nfunction getProviderSnippet(\n\tecosystem: DetectedEcosystem,\n\tsourceLocale: string,\n): { file: string; code: string } | null {\n\tswitch (ecosystem) {\n\t\tcase \"react\":\n\t\t\treturn {\n\t\t\t\tfile: \"your root layout or App component\",\n\t\t\t\tcode: `import { VocoderProvider } from '@vocoder/react';\n\n<VocoderProvider defaultLocale=\"${sourceLocale}\">\n {children}\n</VocoderProvider>`,\n\t\t\t};\n\n\t\tcase \"vue\":\n\t\t\treturn {\n\t\t\t\tfile: \"your app entry\",\n\t\t\t\tcode: `import { createVocoder } from '@vocoder/vue';\n\nconst vocoder = createVocoder({\n defaultLocale: '${sourceLocale}',\n});\n\napp.use(vocoder);`,\n\t\t\t};\n\n\t\tcase \"svelte\":\n\t\t\treturn {\n\t\t\t\tfile: \"your root layout\",\n\t\t\t\tcode: `<script>\n import { VocoderProvider } from '@vocoder/svelte';\n</script>\n\n<VocoderProvider defaultLocale=\"${sourceLocale}\">\n <slot />\n</VocoderProvider>`,\n\t\t\t};\n\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nfunction getWrapSnippet(ecosystem: DetectedEcosystem): { code: string } {\n\tswitch (ecosystem) {\n\t\tcase \"react\":\n\t\t\treturn {\n\t\t\t\tcode: `import { T } from '@vocoder/react';\n\n<T>Hello, world!</T>`,\n\t\t\t};\n\n\t\tcase \"vue\":\n\t\t\treturn {\n\t\t\t\tcode: `<template>\n <T>Hello, world!</T>\n</template>\n\n<script setup>\nimport { T } from '@vocoder/vue';\n</script>`,\n\t\t\t};\n\n\t\tcase \"svelte\":\n\t\t\treturn {\n\t\t\t\tcode: `<script>\n import { T } from '@vocoder/svelte';\n</script>\n\n<T>Hello, world!</T>`,\n\t\t\t};\n\n\t\tdefault:\n\t\t\treturn {\n\t\t\t\tcode: `// Wrap translatable strings with <T>\n<T>Hello, world!</T>`,\n\t\t\t};\n\t}\n}\n","export { type ExtractedString, StringExtractor } from \"@vocoder/extractor\";\n"],"mappings":"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","names":[]}
package/dist/lib.d.mts CHANGED
@@ -1,48 +1,37 @@
1
- type EffectiveSyncMode = 'required' | 'best-effort';
1
+ export { ExtractedString, StringExtractor } from '@vocoder/extractor';
2
+
3
+ type EffectiveSyncMode = "required" | "best-effort";
2
4
  interface SyncPolicyConfig {
3
5
  blockingBranches: string[];
4
6
  blockingMode: EffectiveSyncMode;
5
7
  nonBlockingMode: EffectiveSyncMode;
6
8
  defaultMaxWaitMs: number;
7
9
  }
8
- interface BranchTriggerConfig {
9
- pattern: string;
10
- triggers: string[];
11
- }
12
10
  interface APIProjectConfig {
13
11
  projectName: string;
14
12
  organizationName: string;
15
13
  sourceLocale: string;
16
14
  targetLocales: string[];
17
- branchTriggers: BranchTriggerConfig[];
18
- /** Primary branch derived from branchTriggers (first non-wildcard pattern) */
15
+ targetBranches: string[];
19
16
  primaryBranch?: string;
20
17
  syncPolicy: SyncPolicyConfig;
21
18
  }
22
- interface ExtractedString {
23
- key: string;
24
- text: string;
25
- file: string;
26
- line: number;
27
- context?: string;
28
- formality?: 'formal' | 'informal' | 'neutral' | 'auto';
29
- }
30
19
  interface TranslationBatchResponse {
31
20
  batchId: string;
32
21
  newStrings: number;
33
22
  deletedStrings?: number;
34
23
  totalStrings: number;
35
- status: 'PENDING' | 'TRANSLATING' | 'COMPLETED' | 'FAILED' | 'UP_TO_DATE';
24
+ status: "PENDING" | "TRANSLATING" | "COMPLETED" | "FAILED" | "UP_TO_DATE";
36
25
  noChanges?: boolean;
37
26
  estimatedTime?: number;
38
27
  effectiveMode?: EffectiveSyncMode;
39
- queueStatus?: 'QUEUED' | 'PROCESSING' | 'COMPLETED' | 'FAILED';
28
+ queueStatus?: "QUEUED" | "PROCESSING" | "COMPLETED" | "FAILED";
40
29
  snapshotAvailable?: boolean;
41
30
  latestCompletedBatchId?: string;
42
31
  translations?: Record<string, Record<string, string>>;
43
32
  }
44
33
  interface TranslationStatusResponse {
45
- status: 'PENDING' | 'TRANSLATING' | 'COMPLETED' | 'FAILED';
34
+ status: "PENDING" | "TRANSLATING" | "COMPLETED" | "FAILED";
46
35
  progress: number;
47
36
  jobs?: Array<{
48
37
  locale: string;
@@ -52,12 +41,12 @@ interface TranslationStatusResponse {
52
41
  translations?: Record<string, Record<string, string>>;
53
42
  localeMetadata?: Record<string, {
54
43
  nativeName: string;
55
- dir?: 'rtl';
44
+ dir?: "rtl";
56
45
  }>;
57
46
  errorMessage?: string;
58
47
  }
59
48
  interface TranslationSnapshotResponse {
60
- status: 'FOUND' | 'NOT_FOUND';
49
+ status: "FOUND" | "NOT_FOUND";
61
50
  branch: string;
62
51
  sourceLocale?: string;
63
52
  targetLocales?: string[];
@@ -66,12 +55,12 @@ interface TranslationSnapshotResponse {
66
55
  translations?: Record<string, Record<string, string>>;
67
56
  localeMetadata?: Record<string, {
68
57
  nativeName: string;
69
- dir?: 'rtl';
58
+ dir?: "rtl";
70
59
  }>;
71
60
  }
72
61
  interface LimitErrorResponse {
73
- errorCode: 'LIMIT_EXCEEDED' | 'INSUFFICIENT_CREDITS';
74
- limitType: 'organizations' | 'projects' | 'git_connections' | 'members' | 'providers' | 'translation_chars' | 'source_strings' | 'credits';
62
+ errorCode: "LIMIT_EXCEEDED" | "INSUFFICIENT_CREDITS";
63
+ limitType: "organizations" | "projects" | "git_connections" | "members" | "providers" | "translation_chars" | "source_strings" | "credits";
75
64
  planId: string;
76
65
  current: number;
77
66
  required: number;
@@ -79,57 +68,16 @@ interface LimitErrorResponse {
79
68
  message: string;
80
69
  }
81
70
  interface SyncPolicyErrorResponse {
82
- errorCode: 'BRANCH_NOT_ALLOWED' | 'PROJECT_REPOSITORY_MISMATCH';
71
+ errorCode: "BRANCH_NOT_ALLOWED" | "PROJECT_REPOSITORY_MISMATCH";
83
72
  message: string;
84
73
  branch?: string;
85
74
  boundRepoLabel?: string | null;
86
75
  boundScopePath?: string | null;
87
76
  }
88
77
 
89
- /**
90
- * Extract translatable strings from source files
91
- *
92
- * NOTE: This is a simplified version for the CLI MVP.
93
- * Eventually this logic should be moved to a shared @vocoder/extraction package
94
- * that can be used by both the CLI and the backend.
95
- */
96
- declare class StringExtractor {
97
- /**
98
- * Extract strings from all files matching the pattern(s)
99
- *
100
- * @param pattern - Glob pattern(s) to include
101
- * @param projectRoot - Project root directory
102
- * @param excludePattern - Glob pattern(s) to exclude (optional)
103
- */
104
- extractFromProject(pattern: string | string[], projectRoot?: string, excludePattern?: string | string[]): Promise<ExtractedString[]>;
105
- /**
106
- * Extract strings from a single file
107
- */
108
- private extractFromFile;
109
- /**
110
- * Extract text from template literal
111
- * Converts template literals like `Hello ${name}` to `Hello {name}`
112
- */
113
- private extractTemplateText;
114
- /**
115
- * Extract text content from JSX children
116
- */
117
- private extractTextContent;
118
- /**
119
- * Get string value from JSX attribute
120
- * Handles both string literals and template literals
121
- */
122
- private getStringAttribute;
123
- /**
124
- * Deduplicate strings (keep first occurrence)
125
- */
126
- private deduplicateStrings;
127
- private generateStableKey;
128
- }
129
-
130
- type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun';
131
- type DetectedFramework = 'nextjs' | 'vite' | 'remix' | 'nuxt' | 'sveltekit' | 'gatsby' | 'angular' | null;
132
- type DetectedEcosystem = 'react' | 'vue' | 'svelte' | 'angular' | null;
78
+ type PackageManager = "pnpm" | "npm" | "yarn" | "bun";
79
+ type DetectedFramework = "nextjs" | "vite" | "remix" | "nuxt" | "sveltekit" | "gatsby" | "angular" | null;
80
+ type DetectedEcosystem = "react" | "vue" | "svelte" | "angular" | null;
133
81
  interface LocalDetectionResult {
134
82
  ecosystem: DetectedEcosystem;
135
83
  framework: DetectedFramework;
@@ -174,10 +122,7 @@ declare function getSetupSnippets(params: {
174
122
  framework: DetectedFramework;
175
123
  ecosystem: DetectedEcosystem;
176
124
  sourceLocale: string;
177
- branchTriggers: Array<{
178
- pattern: string;
179
- triggers: string[];
180
- }>;
125
+ targetBranches: string[];
181
126
  }): SetupSnippets;
182
127
 
183
- export { type APIProjectConfig, type DetectedEcosystem, type DetectedFramework, type ExtractedString, type LimitErrorResponse, type LocalDetectionResult, type PackageManager, type SetupSnippets, StringExtractor, type SyncPolicyConfig, type SyncPolicyErrorResponse, type TranslationBatchResponse, type TranslationSnapshotResponse, type TranslationStatusResponse, buildInstallCommand, detectLocalEcosystem, getPackagesToInstall, getSetupSnippets };
128
+ export { type APIProjectConfig, type DetectedEcosystem, type DetectedFramework, type LimitErrorResponse, type LocalDetectionResult, type PackageManager, type SetupSnippets, type SyncPolicyConfig, type SyncPolicyErrorResponse, type TranslationBatchResponse, type TranslationSnapshotResponse, type TranslationStatusResponse, buildInstallCommand, detectLocalEcosystem, getPackagesToInstall, getSetupSnippets };
package/dist/lib.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  detectLocalEcosystem,
5
5
  getPackagesToInstall,
6
6
  getSetupSnippets
7
- } from "./chunk-KPIT5ETY.mjs";
7
+ } from "./chunk-OFQLREXF.mjs";
8
8
  export {
9
9
  StringExtractor,
10
10
  buildInstallCommand,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vocoder/cli",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "CLI tool for Vocoder translation workflow",
5
5
  "files": [
6
6
  "dist"
@@ -38,35 +38,29 @@
38
38
  "bugs": {
39
39
  "url": "https://github.com/vocoder/vocoder-sdk/issues"
40
40
  },
41
- "scripts": {
42
- "build": "tsup",
43
- "dev": "tsup --watch",
44
- "watch": "tsup --watch",
45
- "test": "vitest run --exclude 'src/__tests__/integration/**'",
46
- "test:watch": "vitest",
47
- "test:unit": "vitest run --exclude 'src/__tests__/integration/**'",
48
- "test:integration": "RUN_INTEGRATION=true vitest run src/__tests__/integration",
49
- "typecheck": "tsc --noEmit"
50
- },
51
41
  "dependencies": {
52
- "@babel/core": "^7.26.0",
53
- "@babel/parser": "^7.26.0",
54
- "@babel/traverse": "^7.26.0",
55
- "@babel/types": "^7.26.0",
56
42
  "@clack/core": "0.4.1",
57
43
  "@clack/prompts": "^0.9.1",
58
44
  "chalk": "^5.3.0",
59
45
  "commander": "^11.1.0",
60
46
  "dotenv": "^16.3.1",
61
- "glob": "^10.3.10",
62
- "tsx": "^4.7.0"
47
+ "tsx": "^4.7.0",
48
+ "@vocoder/extractor": "0.1.1"
63
49
  },
64
50
  "devDependencies": {
65
- "@types/babel__core": "^7.20.5",
66
- "@types/babel__traverse": "^7.20.6",
67
51
  "@types/node": "^20.19.9",
68
52
  "tsup": "^8.0.0",
69
53
  "typescript": "^5.4.0",
70
54
  "vitest": "^1.0.0"
55
+ },
56
+ "scripts": {
57
+ "build": "tsup",
58
+ "dev": "tsup --watch",
59
+ "watch": "tsup --watch",
60
+ "test": "vitest run --exclude 'src/__tests__/integration/**'",
61
+ "test:watch": "vitest",
62
+ "test:unit": "vitest run --exclude 'src/__tests__/integration/**'",
63
+ "test:integration": "RUN_INTEGRATION=true vitest run src/__tests__/integration",
64
+ "typecheck": "tsc --noEmit"
71
65
  }
72
- }
66
+ }