@lingo.dev/cli 1.0.0 → 1.0.1
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/guides/api.md +3 -3
- package/guides/index.md +1 -1
- package/guides/migrate.md +1 -1
- package/guides/setup.md +2 -2
- package/package.json +3 -4
- package/dist/bin.js +0 -108
- package/dist/flush-telemetry.js +0 -38
- package/dist/index.js +0 -3
- package/dist/renderer-DvB06WWF.js +0 -1533
- package/dist/server-BxEBM9NU.js +0 -847
- package/dist/update-C-JdpZwG.js +0 -816
package/guides/api.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
## Translation helpers (context required)
|
|
7
7
|
```tsx
|
|
8
8
|
l.text(source, { context, values? })
|
|
9
|
-
l.
|
|
9
|
+
l.rich(source, { context, tags?, values? })
|
|
10
10
|
l.plural(count, { one, other, zero?, two?, few?, many? }, { context })
|
|
11
11
|
l.select(value, { ...forms, other }, { context })
|
|
12
12
|
```
|
|
@@ -15,13 +15,13 @@ Examples:
|
|
|
15
15
|
```tsx
|
|
16
16
|
l.text("Save", { context: "Form submit button" })
|
|
17
17
|
l.text("Hello, {name}!", { context: "Dashboard welcome", values: { name } })
|
|
18
|
-
l.
|
|
18
|
+
l.rich("Read <link>docs</link>", { context: "Footer link", tags: { link: c => <a href="/docs">{c}</a> } })
|
|
19
19
|
l.plural(items, { one: "# item", other: "# items" }, { context: "Cart count" })
|
|
20
20
|
l.select(role, { admin: "Admin", user: "User", other: "Guest" }, { context: "Nav label" })
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Context rules:
|
|
24
|
-
1) Always required on text/
|
|
24
|
+
1) Always required on text/rich/plural/select.
|
|
25
25
|
2) Describes where/how the string is used ("Checkout CTA", "Toast error").
|
|
26
26
|
3) Same source + different context = different translation key.
|
|
27
27
|
|
package/guides/index.md
CHANGED
|
@@ -4,7 +4,7 @@ Use `npx @lingo.dev/cli guide <name>` to view a guide.
|
|
|
4
4
|
|
|
5
5
|
Available guides:
|
|
6
6
|
- `setup` – first-time setup for @lingo.dev/react (Next.js Pages Router focus, works for React too)
|
|
7
|
-
- `api` – API reference for `l.text`, `l.
|
|
7
|
+
- `api` – API reference for `l.text`, `l.rich`, `l.plural`, `l.select`, formatting helpers
|
|
8
8
|
- `migrate` – migrate from next-intl, react-intl, i18next, lingui to @lingo.dev/react
|
|
9
9
|
|
|
10
10
|
Always finish workflows with `npx @lingo.dev/cli check` (or `pnpm lingo check`) and expect 0 issues.
|
package/guides/migrate.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|------------------|----------------------------|---------------------------------|---------------------------|------------------------------------------------------|
|
|
11
11
|
| Plain text | `t("key")` | `formatMessage({ id })` | `t("key")` | `l.text("source", { context: "..." })` |
|
|
12
12
|
| With values | `t("key", { name })` | `formatMessage({ id }, { name })`| `t("key", { name })` | `l.text("Hello, {name}", { context: "...", values: { name } })` |
|
|
13
|
-
| Rich text | `t.rich("key", { link })`| `<FormattedMessage components>` | `<Trans components>` | `l.
|
|
13
|
+
| Rich text | `t.rich("key", { link })`| `<FormattedMessage components>` | `<Trans components>` | `l.rich("Click <link>here</link>", { context: "...", tags: { link } })` |
|
|
14
14
|
| Plurals | ICU in JSON | `<FormattedPlural>` | ICU in JSON | `l.plural(count, { one: "# item", other: "# items" }, { context: "..." })` |
|
|
15
15
|
| Provider | `NextIntlProvider` | `IntlProvider` | `I18nextProvider` | `withLingoApp` (wraps LingoProvider) |
|
|
16
16
|
| Hook | `useTranslations()` | `useIntl()` | `useTranslation()` | `useLingo()` |
|
package/guides/setup.md
CHANGED
|
@@ -65,11 +65,11 @@ const l = useLingo();
|
|
|
65
65
|
<p>{l.text("The best tool for teams.", { context: "Hero subheading" })}</p>
|
|
66
66
|
<button>{l.text("Get started", { context: "Hero CTA" })}</button>
|
|
67
67
|
```
|
|
68
|
-
Context is REQUIRED on l.text/l.
|
|
68
|
+
Context is REQUIRED on l.text/l.rich/l.plural/l.select.
|
|
69
69
|
|
|
70
70
|
### API cheatsheet
|
|
71
71
|
- `l.text(source, { context, values? })`
|
|
72
|
-
- `l.
|
|
72
|
+
- `l.rich(source, { context, tags?, values? })`
|
|
73
73
|
- `l.plural(count, forms, { context })`
|
|
74
74
|
- `l.select(value, forms, { context })`
|
|
75
75
|
- Formatting: `l.num`, `l.currency`, `l.percent`, `l.date`, `l.time`, `l.relative`, `l.list`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lingo.dev/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"lingo": "./dist/bin.js"
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"posthog-node": "^5.28.2",
|
|
34
34
|
"semver": "^7.7.4",
|
|
35
35
|
"zod": "^4.0.0",
|
|
36
|
-
"@lingo.dev/spec": "1.0.
|
|
36
|
+
"@lingo.dev/spec": "1.0.1"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@babel/types": "^7.29.0",
|
|
@@ -49,7 +49,6 @@
|
|
|
49
49
|
"build": "tsdown",
|
|
50
50
|
"typecheck": "tsc --noEmit",
|
|
51
51
|
"test": "vitest run",
|
|
52
|
-
"cli": "node --import tsx/esm src/bin.ts"
|
|
53
|
-
"dev": "tsdown && node dist/bin.js"
|
|
52
|
+
"cli": "node --import tsx/esm src/bin.ts"
|
|
54
53
|
}
|
|
55
54
|
}
|
package/dist/bin.js
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { a as extractCommandInfo, b as ApiClientLive, d as subcommands, f as TelemetryService, g as cancel, i as collectOptions, l as helpConfig, m as trackCommand, n as renderRootHelp, o as unwrapCommand, p as TelemetryServiceLive, s as cliConfig, t as renderCommandHelp, u as run, w as AuthServiceLive, x as AuthContext, y as ConfigServiceLive } from "./renderer-DvB06WWF.js";
|
|
2
|
-
import { n as UpdateService, r as UpdateServiceLive } from "./update-C-JdpZwG.js";
|
|
3
|
-
import { AutoCorrect, CliConfig, ValidationError } from "@effect/cli";
|
|
4
|
-
import { NodeContext, NodeRuntime } from "@effect/platform-node";
|
|
5
|
-
import { Cause, Effect, Layer, pipe } from "effect";
|
|
6
|
-
import pc from "picocolors";
|
|
7
|
-
//#region src/errors.ts
|
|
8
|
-
const GLOBAL_FLAGS = new Set(["--api-key"]);
|
|
9
|
-
/**
|
|
10
|
-
* Finds the closest known flag for a typo using Levenshtein distance.
|
|
11
|
-
* Returns the suggestion string (e.g. "--json") or undefined if no close match.
|
|
12
|
-
*/
|
|
13
|
-
function findFlagSuggestion(userArgs, subcommands, cliConfig) {
|
|
14
|
-
const commandName = userArgs[0];
|
|
15
|
-
if (!commandName) return void 0;
|
|
16
|
-
const match = subcommands.find((cmd) => extractCommandInfo(cmd.descriptor).name === commandName);
|
|
17
|
-
if (!match) return void 0;
|
|
18
|
-
const knownFlags = collectKnownFlags(match.descriptor);
|
|
19
|
-
const userFlags = userArgs.slice(1).filter((a) => a.startsWith("-")).map((a) => a.split("=")[0]);
|
|
20
|
-
for (const flag of userFlags) {
|
|
21
|
-
if (GLOBAL_FLAGS.has(flag) || knownFlags.has(flag)) continue;
|
|
22
|
-
const suggestion = closest(flag, knownFlags, cliConfig);
|
|
23
|
-
if (suggestion) return suggestion;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Finds the closest known subcommand for a typo using Levenshtein distance.
|
|
28
|
-
*/
|
|
29
|
-
function findSubcommandSuggestion(commandName, subcommands, cliConfig) {
|
|
30
|
-
return closest(commandName, new Set(subcommands.map((cmd) => extractCommandInfo(cmd.descriptor).name)), cliConfig);
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Formats the hint lines shown after a validation error.
|
|
34
|
-
* When commandName is provided, hints at command-level help; otherwise root help.
|
|
35
|
-
*/
|
|
36
|
-
function formatValidationHint(cliName, commandName, suggestion) {
|
|
37
|
-
const lines = [];
|
|
38
|
-
if (suggestion) lines.push(` ${pc.yellow("Tip:")} Did you mean ${pc.bold(suggestion)}?`);
|
|
39
|
-
const helpCmd = commandName ? `${cliName} ${commandName} --help` : `${cliName} --help`;
|
|
40
|
-
const helpTarget = commandName ? "available options" : "available commands";
|
|
41
|
-
lines.push(` Run ${pc.bold(helpCmd)} for ${helpTarget}.`);
|
|
42
|
-
return lines.join("\n");
|
|
43
|
-
}
|
|
44
|
-
function collectKnownFlags(descriptor) {
|
|
45
|
-
const options = collectOptions(unwrapCommand(descriptor).options);
|
|
46
|
-
const flags = /* @__PURE__ */ new Set();
|
|
47
|
-
for (const opt of options) {
|
|
48
|
-
flags.add(`--${opt.long}`);
|
|
49
|
-
if (opt.short) flags.add(`-${opt.short}`);
|
|
50
|
-
}
|
|
51
|
-
return flags;
|
|
52
|
-
}
|
|
53
|
-
function closest(input, candidates, config) {
|
|
54
|
-
let best;
|
|
55
|
-
let bestDist = Infinity;
|
|
56
|
-
for (const candidate of candidates) {
|
|
57
|
-
const dist = AutoCorrect.levensteinDistance(input, candidate, config);
|
|
58
|
-
if (dist < bestDist) {
|
|
59
|
-
bestDist = dist;
|
|
60
|
-
best = candidate;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return bestDist <= 2 ? best : void 0;
|
|
64
|
-
}
|
|
65
|
-
//#endregion
|
|
66
|
-
//#region src/bin.ts
|
|
67
|
-
process.on("SIGINT", () => process.exit(0));
|
|
68
|
-
process.on("SIGTERM", () => process.exit(0));
|
|
69
|
-
const args = process.argv;
|
|
70
|
-
const userArgs = args.slice(2).filter((a) => a !== "--");
|
|
71
|
-
if (userArgs[0] === "mcp") import("./server-BxEBM9NU.js").then((m) => m.startMcpServer()).catch((e) => {
|
|
72
|
-
process.stderr.write(`MCP server error: ${e}\n`);
|
|
73
|
-
process.exit(1);
|
|
74
|
-
});
|
|
75
|
-
else {
|
|
76
|
-
let globalApiKey;
|
|
77
|
-
const apiKeyIdx = userArgs.indexOf("--api-key");
|
|
78
|
-
if (apiKeyIdx !== -1 && userArgs[apiKeyIdx + 1]) globalApiKey = userArgs[apiKeyIdx + 1];
|
|
79
|
-
const isHelp = userArgs.includes("--help") || userArgs.includes("-h") || userArgs.length === 0;
|
|
80
|
-
if (process.env.npm_lifecycle_event === "npx" || process.env.npm_execpath?.includes("npx") || process.env.npm_execpath?.includes("dlx")) process.stderr.write(` ${pc.dim("Tip: install globally for faster startup:")} npm i -g @lingo.dev/cli\n\n`);
|
|
81
|
-
if (isHelp) {
|
|
82
|
-
const subcommandName = userArgs.filter((a) => a !== "--help" && a !== "-h")[0];
|
|
83
|
-
const match = subcommands.find((cmd) => extractCommandInfo(cmd.descriptor).name === subcommandName);
|
|
84
|
-
if (match) console.log(renderCommandHelp(helpConfig, match.descriptor));
|
|
85
|
-
else console.log(renderRootHelp(helpConfig, subcommands.map((cmd) => cmd.descriptor)));
|
|
86
|
-
} else {
|
|
87
|
-
const AuthContextLive = Layer.succeed(AuthContext, { apiKeyFlag: globalApiKey });
|
|
88
|
-
const authLayer = pipe(AuthServiceLive, Layer.provide(AuthContextLive));
|
|
89
|
-
const apiLayer = pipe(ApiClientLive, Layer.provide(authLayer));
|
|
90
|
-
const telemetryLayer = pipe(TelemetryServiceLive, Layer.provide(authLayer), Layer.provide(apiLayer), Layer.provide(ConfigServiceLive));
|
|
91
|
-
const appLayer = pipe(Layer.merge(NodeContext.layer, CliConfig.layer(cliConfig)), Layer.merge(ConfigServiceLive), Layer.merge(authLayer), Layer.merge(apiLayer), Layer.merge(telemetryLayer), Layer.merge(UpdateServiceLive));
|
|
92
|
-
const commandName = userArgs[0] ?? "unknown";
|
|
93
|
-
const flagNames = userArgs.filter((a) => a.startsWith("--")).map((a) => a.split("=")[0]);
|
|
94
|
-
const showUpdateNotification = commandName === "update" ? Effect.void : pipe(UpdateService, Effect.flatMap((u) => u.check), Effect.tap((info) => info ? Effect.sync(() => {
|
|
95
|
-
process.stderr.write(`\n Update available: ${pc.dim(info.current)} → ${pc.green(info.latest)}\n Run ${pc.bold("lingo update")} to upgrade\n`);
|
|
96
|
-
}) : Effect.void), Effect.catchAll(() => Effect.void));
|
|
97
|
-
pipe(run(args), trackCommand(commandName, flagNames), Effect.tap(() => showUpdateNotification), Effect.catchTag("PromptCancelledError", () => cancel("Cancelled.")), Effect.catchIf(ValidationError.isValidationError, (e) => {
|
|
98
|
-
const isSubcommandError = ValidationError.isCommandMismatch(e);
|
|
99
|
-
const suggestion = ValidationError.isInvalidValue(e) ? findFlagSuggestion(userArgs, subcommands, cliConfig) : isSubcommandError ? findSubcommandSuggestion(commandName, subcommands, cliConfig) : void 0;
|
|
100
|
-
return Effect.sync(() => process.stderr.write(formatValidationHint(helpConfig.name, isSubcommandError ? void 0 : commandName, suggestion) + "\n"));
|
|
101
|
-
}), Effect.tapErrorCause((cause) => Cause.isInterruptedOnly(cause) ? Effect.void : pipe(Effect.all([Effect.sync(() => process.stderr.write(`\n ${pc.red("Error:")} Something went wrong. Please try again.\n`)), pipe(TelemetryService, Effect.flatMap((t) => pipe(t.capture("cli_error", {
|
|
102
|
-
command: commandName,
|
|
103
|
-
error: Cause.pretty(cause)
|
|
104
|
-
}), Effect.andThen(t.shutdown))))]), Effect.catchAll(() => Effect.void))), Effect.tap(() => pipe(TelemetryService, Effect.flatMap((t) => t.shutdown), Effect.catchAll(() => Effect.void))), Effect.provide(appLayer), NodeRuntime.runMain({ disableErrorReporting: true }));
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
//#endregion
|
|
108
|
-
export {};
|
package/dist/flush-telemetry.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import { PostHog } from "posthog-node";
|
|
3
|
-
//#region src/flush-telemetry.ts
|
|
4
|
-
/**
|
|
5
|
-
* Detached subprocess that sends buffered telemetry events to PostHog.
|
|
6
|
-
* Spawned by TelemetryService.shutdown -- runs after the main CLI process exits.
|
|
7
|
-
*/
|
|
8
|
-
const file = process.argv[2];
|
|
9
|
-
if (!file) process.exit(0);
|
|
10
|
-
let payload;
|
|
11
|
-
try {
|
|
12
|
-
payload = JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
13
|
-
} catch {
|
|
14
|
-
process.exit(0);
|
|
15
|
-
}
|
|
16
|
-
const client = new PostHog(payload.apiKey, {
|
|
17
|
-
host: payload.host,
|
|
18
|
-
flushAt: payload.events.length || 1,
|
|
19
|
-
flushInterval: 0,
|
|
20
|
-
requestTimeout: 5e3,
|
|
21
|
-
fetchRetryCount: 1
|
|
22
|
-
});
|
|
23
|
-
if (payload.identifyTraits) client.identify({
|
|
24
|
-
distinctId: payload.distinctId,
|
|
25
|
-
properties: payload.identifyTraits
|
|
26
|
-
});
|
|
27
|
-
for (const event of payload.events) client.capture({
|
|
28
|
-
distinctId: payload.distinctId,
|
|
29
|
-
event: event.event,
|
|
30
|
-
properties: event.properties,
|
|
31
|
-
timestamp: new Date(event.timestamp)
|
|
32
|
-
});
|
|
33
|
-
await client.shutdown(1e4);
|
|
34
|
-
try {
|
|
35
|
-
fs.unlinkSync(file);
|
|
36
|
-
} catch {}
|
|
37
|
-
//#endregion
|
|
38
|
-
export {};
|
package/dist/index.js
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import { C as AuthService, S as AuthError, _ as ConfigError, a as extractCommandInfo, c as command, d as subcommands, h as PromptCancelledError, i as collectOptions, l as helpConfig, n as renderRootHelp, r as collectArgs, s as cliConfig, t as renderCommandHelp, u as run, v as ConfigService, w as AuthServiceLive, x as AuthContext, y as ConfigServiceLive } from "./renderer-DvB06WWF.js";
|
|
2
|
-
import { n as UpdateService, r as UpdateServiceLive, s as VERSION, t as UpdateError } from "./update-C-JdpZwG.js";
|
|
3
|
-
export { AuthContext, AuthError, AuthService, AuthServiceLive, ConfigError, ConfigService, ConfigServiceLive, PromptCancelledError, UpdateError, UpdateService, UpdateServiceLive, VERSION, cliConfig, collectArgs, collectOptions, command, extractCommandInfo, helpConfig, renderCommandHelp, renderRootHelp, run, subcommands };
|