@inlang/paraglide-js 2.26.0 → 2.28.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 +11 -1
- package/dist/bundler-plugins/per-locale-build/index.js +14 -7
- package/dist/bundler-plugins/per-locale-build/index.js.map +1 -1
- package/dist/bundler-plugins/per-locale-build/vite-environments.test.js +237 -4
- package/dist/bundler-plugins/per-locale-build/vite-environments.test.js.map +1 -1
- package/dist/bundler-plugins/unplugin.d.ts +1 -1
- package/dist/bundler-plugins/unplugin.js +79 -10
- package/dist/bundler-plugins/unplugin.js.map +1 -1
- package/dist/bundler-plugins/unplugin.test.js +334 -1
- package/dist/bundler-plugins/unplugin.test.js.map +1 -1
- package/dist/bundler-plugins/vite.js +2 -1
- package/dist/bundler-plugins/vite.js.map +1 -1
- package/dist/bundler-plugins/vite.test.js +1 -1
- package/dist/bundler-plugins/vite.test.js.map +1 -1
- package/dist/cli/commands/compile/command.js +13 -0
- package/dist/cli/commands/compile/command.js.map +1 -1
- package/dist/cli/commands/compile/command.test.js +87 -3
- package/dist/cli/commands/compile/command.test.js.map +1 -1
- package/dist/cli/commands/init/command.d.ts +5 -0
- package/dist/cli/commands/init/command.js +216 -44
- package/dist/cli/commands/init/command.js.map +1 -1
- package/dist/cli/commands/init/command.test.d.ts +1 -0
- package/dist/cli/commands/init/command.test.js +123 -0
- package/dist/cli/commands/init/command.test.js.map +1 -0
- package/dist/cli/defaults.js +2 -2
- package/dist/cli/defaults.js.map +1 -1
- package/dist/cli/init-options.d.ts +11 -0
- package/dist/cli/init-options.js +2 -0
- package/dist/cli/init-options.js.map +1 -0
- package/dist/cli/steps/add-inlang-check.d.ts +19 -0
- package/dist/cli/steps/add-inlang-check.js +60 -0
- package/dist/cli/steps/add-inlang-check.js.map +1 -0
- package/dist/cli/steps/add-inlang-check.test.d.ts +1 -0
- package/dist/cli/steps/add-inlang-check.test.js +88 -0
- package/dist/cli/steps/add-inlang-check.test.js.map +1 -0
- package/dist/cli/steps/add-message-lint.d.ts +34 -0
- package/dist/cli/steps/add-message-lint.js +105 -0
- package/dist/cli/steps/add-message-lint.js.map +1 -0
- package/dist/cli/steps/add-message-lint.test.d.ts +1 -0
- package/dist/cli/steps/add-message-lint.test.js +74 -0
- package/dist/cli/steps/add-message-lint.test.js.map +1 -0
- package/dist/cli/steps/add-vite-plugin.d.ts +18 -0
- package/dist/cli/steps/add-vite-plugin.js +34 -6
- package/dist/cli/steps/add-vite-plugin.js.map +1 -1
- package/dist/cli/steps/add-vite-plugin.test.js +25 -9
- package/dist/cli/steps/add-vite-plugin.test.js.map +1 -1
- package/dist/cli/steps/agents-md-suggestion.d.ts +11 -0
- package/dist/cli/steps/agents-md-suggestion.js +19 -0
- package/dist/cli/steps/agents-md-suggestion.js.map +1 -0
- package/dist/cli/steps/agents-md-suggestion.test.d.ts +1 -0
- package/dist/cli/steps/agents-md-suggestion.test.js +36 -0
- package/dist/cli/steps/agents-md-suggestion.test.js.map +1 -0
- package/dist/cli/steps/check-for-uncomitted-changes.d.ts +3 -0
- package/dist/cli/steps/check-for-uncomitted-changes.js +15 -3
- package/dist/cli/steps/check-for-uncomitted-changes.js.map +1 -1
- package/dist/cli/steps/detect-bundler.d.ts +1 -0
- package/dist/cli/steps/detect-bundler.js +7 -1
- package/dist/cli/steps/detect-bundler.js.map +1 -1
- package/dist/cli/steps/initialize-inlang-project.d.ts +4 -3
- package/dist/cli/steps/initialize-inlang-project.js +60 -157
- package/dist/cli/steps/initialize-inlang-project.js.map +1 -1
- package/dist/cli/steps/maybe-add-machine-translation.d.ts +4 -0
- package/dist/cli/steps/maybe-add-machine-translation.js +14 -8
- package/dist/cli/steps/maybe-add-machine-translation.js.map +1 -1
- package/dist/cli/steps/maybe-add-sherlock.d.ts +2 -0
- package/dist/cli/steps/maybe-add-sherlock.js +13 -7
- package/dist/cli/steps/maybe-add-sherlock.js.map +1 -1
- package/dist/cli/steps/maybe-add-sherlock.test.d.ts +1 -0
- package/dist/cli/steps/maybe-add-sherlock.test.js +44 -0
- package/dist/cli/steps/maybe-add-sherlock.test.js.map +1 -0
- package/dist/cli/steps/prompt-for-outdir.d.ts +2 -0
- package/dist/cli/steps/prompt-for-outdir.js +4 -0
- package/dist/cli/steps/prompt-for-outdir.js.map +1 -1
- package/dist/cli/steps/update-package-json.js +8 -13
- package/dist/cli/steps/update-package-json.js.map +1 -1
- package/dist/cli/steps/update-ts-config.d.ts +6 -0
- package/dist/cli/steps/update-ts-config.js +27 -4
- package/dist/cli/steps/update-ts-config.js.map +1 -1
- package/dist/cli/utils.d.ts +11 -0
- package/dist/cli/utils.js +21 -0
- package/dist/cli/utils.js.map +1 -1
- package/dist/cli/utils.test.d.ts +1 -0
- package/dist/cli/utils.test.js +16 -0
- package/dist/cli/utils.test.js.map +1 -0
- package/dist/compiler/compile-annotation.d.ts +48 -1
- package/dist/compiler/compile-annotation.js +243 -10
- package/dist/compiler/compile-annotation.js.map +1 -1
- package/dist/compiler/compile-bundle.d.ts +7 -2
- package/dist/compiler/compile-bundle.js +97 -3
- package/dist/compiler/compile-bundle.js.map +1 -1
- package/dist/compiler/compile-bundle.test.js +232 -19
- package/dist/compiler/compile-bundle.test.js.map +1 -1
- package/dist/compiler/compile-lifecycle.test.d.ts +1 -0
- package/dist/compiler/compile-lifecycle.test.js +69 -0
- package/dist/compiler/compile-lifecycle.test.js.map +1 -0
- package/dist/compiler/compile-local-variable.d.ts +3 -0
- package/dist/compiler/compile-local-variable.js +5 -2
- package/dist/compiler/compile-local-variable.js.map +1 -1
- package/dist/compiler/compile-local-variable.test.js +55 -15
- package/dist/compiler/compile-local-variable.test.js.map +1 -1
- package/dist/compiler/compile-message.d.ts +29 -1
- package/dist/compiler/compile-message.exact-number.test.d.ts +1 -0
- package/dist/compiler/compile-message.exact-number.test.js +786 -0
- package/dist/compiler/compile-message.exact-number.test.js.map +1 -0
- package/dist/compiler/compile-message.js +162 -66
- package/dist/compiler/compile-message.js.map +1 -1
- package/dist/compiler/compile-message.locals.test.d.ts +1 -0
- package/dist/compiler/compile-message.locals.test.js +498 -0
- package/dist/compiler/compile-message.locals.test.js.map +1 -0
- package/dist/compiler/compile-message.test.js +113 -22
- package/dist/compiler/compile-message.test.js.map +1 -1
- package/dist/compiler/compile-pattern.d.ts +5 -0
- package/dist/compiler/compile-pattern.js +6 -18
- package/dist/compiler/compile-pattern.js.map +1 -1
- package/dist/compiler/compile-pattern.test.js +169 -5
- package/dist/compiler/compile-pattern.test.js.map +1 -1
- package/dist/compiler/compile-project.js +5 -1
- package/dist/compiler/compile-project.js.map +1 -1
- package/dist/compiler/compile-project.test.js +1260 -185
- package/dist/compiler/compile-project.test.js.map +1 -1
- package/dist/compiler/compile.js +36 -17
- package/dist/compiler/compile.js.map +1 -1
- package/dist/compiler/compile.test.js +71 -1
- package/dist/compiler/compile.test.js.map +1 -1
- package/dist/compiler/compiler-options.d.ts +41 -0
- package/dist/compiler/compiler-options.js.map +1 -1
- package/dist/compiler/create-readme.d.ts +4 -1
- package/dist/compiler/create-readme.js +33 -125
- package/dist/compiler/create-readme.js.map +1 -1
- package/dist/compiler/emit-ts-declarations.interop.test.js +2 -2
- package/dist/compiler/emit-ts-declarations.interop.test.js.map +1 -1
- package/dist/compiler/emit-ts-declarations.test.js +4 -4
- package/dist/compiler/emit-ts-declarations.test.js.map +1 -1
- package/dist/compiler/index.d.ts +1 -1
- package/dist/compiler/index.js +1 -1
- package/dist/compiler/index.js.map +1 -1
- package/dist/compiler/input-alias.d.ts +21 -0
- package/dist/compiler/input-alias.js +42 -0
- package/dist/compiler/input-alias.js.map +1 -0
- package/dist/compiler/input-alias.test.d.ts +1 -0
- package/dist/compiler/input-alias.test.js +58 -0
- package/dist/compiler/input-alias.test.js.map +1 -0
- package/dist/compiler/message-locals.d.ts +46 -0
- package/dist/compiler/message-locals.js +129 -0
- package/dist/compiler/message-locals.js.map +1 -0
- package/dist/compiler/output-structure/locale-modules.js +7 -1
- package/dist/compiler/output-structure/locale-modules.js.map +1 -1
- package/dist/compiler/output-structure/locale-modules.test.js +35 -0
- package/dist/compiler/output-structure/locale-modules.test.js.map +1 -1
- package/dist/compiler/output-structure/message-modules.js +6 -4
- package/dist/compiler/output-structure/message-modules.js.map +1 -1
- package/dist/compiler/output-structure/message-modules.test.js +35 -0
- package/dist/compiler/output-structure/message-modules.test.js.map +1 -1
- package/dist/compiler/registry.d.ts +12 -0
- package/dist/compiler/registry.js +158 -31
- package/dist/compiler/registry.js.map +1 -1
- package/dist/compiler/registry.test.d.ts +1 -0
- package/dist/compiler/registry.test.js +291 -0
- package/dist/compiler/registry.test.js.map +1 -0
- package/dist/compiler/runtime/create-runtime.d.ts +2 -0
- package/dist/compiler/runtime/create-runtime.js +9 -0
- package/dist/compiler/runtime/create-runtime.js.map +1 -1
- package/dist/compiler/runtime/extract-locale-from-cookie.js +8 -1
- package/dist/compiler/runtime/extract-locale-from-cookie.js.map +1 -1
- package/dist/compiler/runtime/extract-locale-from-cookie.test.js +26 -0
- package/dist/compiler/runtime/extract-locale-from-cookie.test.js.map +1 -1
- package/dist/compiler/runtime/extract-locale-from-header.js +47 -5
- package/dist/compiler/runtime/extract-locale-from-header.js.map +1 -1
- package/dist/compiler/runtime/extract-locale-from-header.test.js +58 -0
- package/dist/compiler/runtime/extract-locale-from-header.test.js.map +1 -1
- package/dist/compiler/runtime/get-locale.js +7 -1
- package/dist/compiler/runtime/get-locale.js.map +1 -1
- package/dist/compiler/runtime/get-locale.test.js +95 -0
- package/dist/compiler/runtime/get-locale.test.js.map +1 -1
- package/dist/compiler/runtime/localize-href.d.ts +8 -0
- package/dist/compiler/runtime/localize-href.js +60 -0
- package/dist/compiler/runtime/localize-href.js.map +1 -1
- package/dist/compiler/runtime/localize-href.test.js +227 -1
- package/dist/compiler/runtime/localize-href.test.js.map +1 -1
- package/dist/compiler/runtime/set-locale.js +24 -6
- package/dist/compiler/runtime/set-locale.js.map +1 -1
- package/dist/compiler/runtime/set-locale.test.js +182 -0
- package/dist/compiler/runtime/set-locale.test.js.map +1 -1
- package/dist/compiler/runtime/type.d.ts +2 -0
- package/dist/compiler/runtime/type.js.map +1 -1
- package/dist/compiler/runtime/type.test.js +34 -0
- package/dist/compiler/runtime/type.test.js.map +1 -1
- package/dist/compiler/runtime/variables.d.ts +2 -0
- package/dist/compiler/runtime/variables.js +7 -0
- package/dist/compiler/runtime/variables.js.map +1 -1
- package/dist/compiler/server/create-server-file.js +24 -0
- package/dist/compiler/server/create-server-file.js.map +1 -1
- package/dist/compiler/server/create-server-file.test.js +8 -2
- package/dist/compiler/server/create-server-file.test.js.map +1 -1
- package/dist/compiler/server/middleware.d.ts +8 -2
- package/dist/compiler/server/middleware.js +204 -11
- package/dist/compiler/server/middleware.js.map +1 -1
- package/dist/compiler/server/middleware.test.js +750 -3
- package/dist/compiler/server/middleware.test.js.map +1 -1
- package/dist/eslint/index.d.ts +4 -0
- package/dist/eslint/index.js +8 -0
- package/dist/eslint/index.js.map +1 -0
- package/dist/eslint/no-dynamic-message-access.d.ts +3 -0
- package/dist/eslint/no-dynamic-message-access.js +150 -0
- package/dist/eslint/no-dynamic-message-access.js.map +1 -0
- package/dist/eslint/no-dynamic-message-access.test.d.ts +1 -0
- package/dist/eslint/no-dynamic-message-access.test.js +126 -0
- package/dist/eslint/no-dynamic-message-access.test.js.map +1 -0
- package/dist/eslint/oxlint.test.d.ts +1 -0
- package/dist/eslint/oxlint.test.js +64 -0
- package/dist/eslint/oxlint.test.js.map +1 -0
- package/dist/services/config/config-schema.d.ts +2 -0
- package/dist/services/config/config-schema.js +2 -0
- package/dist/services/config/config-schema.js.map +1 -1
- package/dist/services/config/load-paraglide-config.test.js +12 -0
- package/dist/services/config/load-paraglide-config.test.js.map +1 -1
- package/dist/services/env-variables/index.js +1 -1
- package/dist/services/env-variables/index.js.map +1 -1
- package/dist/services/file-handling/write-output.bench.js +51 -11
- package/dist/services/file-handling/write-output.bench.js.map +1 -1
- package/dist/services/file-handling/write-output.js +60 -20
- package/dist/services/file-handling/write-output.js.map +1 -1
- package/dist/services/file-handling/write-output.test.js +175 -0
- package/dist/services/file-handling/write-output.test.js.map +1 -1
- package/package.json +15 -5
|
@@ -1,43 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Creates the README.md content for the compiled output.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* The README tells coding agents what this generated folder is, what to edit
|
|
5
|
+
* instead, and where inlang tooling ends and Paraglide JS begins.
|
|
5
6
|
* See https://llmstxt.org/ for the format guidelines.
|
|
6
7
|
*/
|
|
7
8
|
export function createReadme(args) {
|
|
8
9
|
const compiledFromLine = args.projectPath
|
|
9
10
|
? `\nCompiled from: \`${args.projectPath}\`\n`
|
|
10
11
|
: "";
|
|
12
|
+
const projectPath = args.projectPath ?? "./project.inlang";
|
|
13
|
+
const gitignoreNote = args.gitignored === false
|
|
14
|
+
? ""
|
|
15
|
+
: " This folder is git-ignored (see `.gitignore`), so don't commit it.";
|
|
11
16
|
return `# Paraglide JS Compiled Output
|
|
12
17
|
|
|
13
|
-
>
|
|
18
|
+
> Generated by [Paraglide JS](https://paraglidejs.com). Do not edit: every file here is overwritten on the next compile.
|
|
14
19
|
${compiledFromLine}
|
|
15
|
-
|
|
16
20
|
## What is this folder?
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
Paraglide JS is a compiler-based i18n library. It compiles an [inlang](https://inlang.com) project into typesafe message functions. Messages and params are type-checked, unused messages are tree-shaken out of the bundle, and the runtime includes locale detection and localized URLs. It targets Vite-based apps (React, TanStack Start/Router, SvelteKit, Vue, Solid, Astro, ...) and has plugins for other bundlers. It also compiles standalone with its CLI (\`npx @inlang/paraglide-js compile\`), without a bundler.
|
|
23
|
+
|
|
24
|
+
## Edit the source, not this folder
|
|
19
25
|
|
|
20
|
-
|
|
26
|
+
- **Text and translations:** the translation files of the inlang project (see \`pathPattern\` in \`${projectPath}/settings.json\`, for example \`messages/{locale}.json\`). Add, rename and fix messages there.
|
|
27
|
+
- **Locales and file format:** \`${projectPath}/settings.json\`.
|
|
28
|
+
- **Compiler options** (\`strategy\`, \`outdir\`, ...): the bundler plugin options or \`${projectPath}/paraglide.config.*\`.
|
|
29
|
+
- **Regenerate:** the bundler plugin (for example \`paraglideVitePlugin\`) recompiles on dev and build. Without a bundler, run \`npx @inlang/paraglide-js compile --project ${projectPath} --outdir <this folder>\`.${gitignoreNote}
|
|
21
30
|
|
|
22
|
-
|
|
23
|
-
- This folder stores compiled i18n message functions.
|
|
24
|
-
- Source translations live outside this folder in your inlang project.
|
|
31
|
+
If a new message has no \`m.*\` function yet, the output is stale: recompile (for example, restart the dev server).
|
|
25
32
|
|
|
26
|
-
|
|
27
|
-
- \`messages.js\` — all message functions
|
|
28
|
-
- \`runtime.js\` — locale utilities
|
|
29
|
-
- \`server.js\` — server-side middleware
|
|
33
|
+
## inlang vs. Paraglide JS
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
35
|
+
- **inlang** owns the localization project: messages, translation files, \`settings.json\`, plugins, and the tooling around them. Use the inlang CLI for [checks](https://inlang.com/m/2qj2w8pu/app-inlang-cli#check) (\`npx @inlang/cli check --project ${projectPath}\` finds missing translations and unused messages) and [machine translation](https://inlang.com/m/2qj2w8pu/app-inlang-cli#machine-translate). Translators can use [Fink](https://inlang.com/m/tdozzpar/app-inlang-finkLocalizationEditor), designers [Parrot](https://inlang.com/m/gkrpgoir/app-parrot-figmaPlugin) in Figma, and developers [Sherlock](https://inlang.com/m/r7kp499g/app-inlang-ideExtension) in VS Code. The project folder has its own README.
|
|
36
|
+
- **Paraglide JS** owns this folder: message functions, the locale runtime, and server middleware.
|
|
37
|
+
- The file format (inlang message format, i18next, ICU MessageFormat, ...) is an inlang plugin. Switching formats doesn't change your \`m.*\` calls. See [file formats](https://paraglidejs.com/file-formats). Switching to a runtime library such as i18next gives up the compiled typesafety and tree-shaking; inlang's checks, machine translation and editors keep working through a format plugin. See the [comparison](https://paraglidejs.com/comparison).
|
|
34
38
|
|
|
35
39
|
\`\`\`
|
|
36
40
|
paraglide/
|
|
37
41
|
├── messages.js # Message exports (import this)
|
|
38
42
|
├── messages/ # Individual message functions
|
|
39
43
|
├── runtime.js # Locale detection & configuration
|
|
40
|
-
├── registry.js #
|
|
44
|
+
├── registry.js # Formatters for plural, number, datetime, date, time, relativetime, icu:pound
|
|
41
45
|
├── server.js # Server-side middleware
|
|
42
46
|
└── .gitignore # Marks folder as generated
|
|
43
47
|
\`\`\`
|
|
@@ -45,129 +49,33 @@ paraglide/
|
|
|
45
49
|
## Usage
|
|
46
50
|
|
|
47
51
|
\`\`\`js
|
|
48
|
-
import
|
|
49
|
-
|
|
50
|
-
// Messages are functions that return localized strings
|
|
51
|
-
m.hello_world(); // "Hello, World!" (in current locale)
|
|
52
|
-
m.greeting({ name: "Sam" }); // "Hello, Sam!"
|
|
52
|
+
import { m } from "./paraglide/messages.js";
|
|
53
53
|
|
|
54
|
-
//
|
|
55
|
-
m.
|
|
54
|
+
m.hello_world(); // "Hello, World!" (current locale)
|
|
55
|
+
m.greeting({ name: "Sam" }); // "Hello, Sam!"
|
|
56
56
|
m.greeting({ name: "Sam" }, { locale: "de" }); // "Hallo, Sam!"
|
|
57
57
|
\`\`\`
|
|
58
58
|
|
|
59
|
-
## Runtime API
|
|
60
|
-
|
|
61
|
-
\`\`\`js
|
|
62
|
-
import { getLocale, getTextDirection, setLocale, locales, baseLocale } from "./paraglide/runtime.js";
|
|
63
|
-
|
|
64
|
-
getLocale(); // Current locale, e.g., "en"
|
|
65
|
-
getTextDirection(); // "ltr" | "rtl" for current locale
|
|
66
|
-
setLocale("de"); // Set locale
|
|
67
|
-
locales; // Available locales, e.g., ["en", "de", "fr"]
|
|
68
|
-
baseLocale; // Default locale, e.g., "en"
|
|
69
|
-
\`\`\`
|
|
70
|
-
|
|
71
|
-
## Strategy
|
|
72
|
-
|
|
73
|
-
The strategy determines how the current locale is detected and persisted:
|
|
74
|
-
|
|
75
|
-
- **Cookie**: Stores locale preference in a cookie.
|
|
76
|
-
- **URL**: Derives locale from URL patterns (e.g., \`/en/about\`, \`en.example.com\`).
|
|
77
|
-
- **GlobalVariable**: Uses a global variable (client-side only).
|
|
78
|
-
- **BaseLocale**: Always returns the base locale.
|
|
79
|
-
|
|
80
|
-
Strategies can be combined. The order defines precedence:
|
|
81
|
-
|
|
82
59
|
\`\`\`js
|
|
83
|
-
|
|
84
|
-
project: "./project.inlang",
|
|
85
|
-
outdir: "./src/paraglide",
|
|
86
|
-
strategy: ["url", "cookie", "baseLocale"],
|
|
87
|
-
});
|
|
88
|
-
\`\`\`
|
|
89
|
-
|
|
90
|
-
See the [strategy documentation](https://paraglidejs.com/strategy) for details.
|
|
91
|
-
|
|
92
|
-
## Markup (Rich Text)
|
|
93
|
-
|
|
94
|
-
Messages can contain markup tags for bold, links, and other inline elements. Translators control where tags appear; developers control how they render.
|
|
95
|
-
|
|
96
|
-
Important:
|
|
97
|
-
- Tag names are app-defined. There is no built-in list of HTML tags.
|
|
98
|
-
- \`{#b}...{/b}\` does not automatically render as \`<b>...</b>\`.
|
|
99
|
-
- Renderers/snippets are looked up by the same tag name used in the message.
|
|
100
|
-
|
|
101
|
-
### Message syntax
|
|
102
|
-
|
|
103
|
-
\`\`\`json
|
|
104
|
-
{
|
|
105
|
-
"cta": "{#link to=|/docs| rel=$relationship @track}Read the docs{/link}",
|
|
106
|
-
"welcome": "{#b}Hi {name}{/b}{#icon/}"
|
|
107
|
-
}
|
|
108
|
-
\`\`\`
|
|
109
|
-
|
|
110
|
-
- \`{#tagName}\` opens a tag, \`{/tagName}\` closes it.
|
|
111
|
-
- \`{#tagName/}\` creates a standalone tag.
|
|
112
|
-
- Options: \`to=|/docs|\` or \`rel=$relationship\` (accessed via \`options.*\`).
|
|
113
|
-
- Attributes: \`@track\` or \`@variant=|hero|\` (accessed via \`attributes.*\`).
|
|
114
|
-
|
|
115
|
-
This is the default inlang message syntax. Paraglide's message format is plugin-based — you can use [ICU MessageFormat 1](https://inlang.com/m/p7c8m1d2/plugin-inlang-icu-messageformat-1), [i18next](https://inlang.com/m/3i8bor92/plugin-inlang-i18next), or other [plugins](https://inlang.com/c/plugins) instead.
|
|
116
|
-
|
|
117
|
-
### Rendering markup
|
|
118
|
-
|
|
119
|
-
Calling the message function still returns **plain text** (markup stripped):
|
|
120
|
-
|
|
121
|
-
\`\`\`js
|
|
122
|
-
m.cta({ relationship: "noopener" }); // "Read the docs"
|
|
123
|
-
\`\`\`
|
|
124
|
-
|
|
125
|
-
To render markup, use the framework adapter or the low-level \`parts()\` API:
|
|
126
|
-
|
|
127
|
-
\`\`\`js
|
|
128
|
-
const parts = m.cta.parts({ relationship: "noopener" });
|
|
129
|
-
// [
|
|
130
|
-
// { type: "markup-start", name: "link", options: { to: "/docs", rel: "noopener" }, attributes: { track: true } },
|
|
131
|
-
// { type: "text", value: "Read the docs" },
|
|
132
|
-
// { type: "markup-end", name: "link" }
|
|
133
|
-
// ]
|
|
134
|
-
\`\`\`
|
|
135
|
-
|
|
136
|
-
Framework adapters provide a \`<ParaglideMessage>\` component that accepts markup renderers:
|
|
137
|
-
|
|
138
|
-
- \`@inlang/paraglide-js-react\`
|
|
139
|
-
- \`@inlang/paraglide-js-vue\`
|
|
140
|
-
- \`@inlang/paraglide-js-svelte\`
|
|
141
|
-
- \`@inlang/paraglide-js-solid\`
|
|
142
|
-
|
|
143
|
-
\`\`\`jsx
|
|
144
|
-
import { ParaglideMessage } from "@inlang/paraglide-js-react"; // or -vue, -svelte, -solid
|
|
60
|
+
import { getLocale, setLocale, locales, baseLocale, getTextDirection } from "./paraglide/runtime.js";
|
|
145
61
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
b: ({ children }) => <b>{children}</b>,
|
|
151
|
-
icon: () => <span aria-hidden="true" className="icon-wave" />,
|
|
152
|
-
}}
|
|
153
|
-
/>
|
|
62
|
+
getLocale(); // e.g. "en"
|
|
63
|
+
setLocale("de"); // reloads the page by default; pass { reload: false } to skip
|
|
64
|
+
locales; // e.g. ["en", "de"]
|
|
65
|
+
getTextDirection(); // "ltr" | "rtl"
|
|
154
66
|
\`\`\`
|
|
155
67
|
|
|
156
|
-
|
|
68
|
+
## Strategy and localized URLs
|
|
157
69
|
|
|
158
|
-
|
|
70
|
+
The compiler option \`strategy\` is an ordered list of how the locale is detected and stored; the first strategy that resolves a locale wins (default \`["cookie", "globalVariable", "baseLocale"]\`). Options include \`"url"\` (localized URLs such as \`/de/about\`), \`"cookie"\`, \`"localStorage"\`, \`"preferredLanguage"\`, \`"globalVariable"\`, \`"baseLocale"\` and \`"custom-*"\`. See [strategy](https://paraglidejs.com/strategy) and [i18n routing](https://paraglidejs.com/i18n-routing).
|
|
159
71
|
|
|
160
|
-
##
|
|
72
|
+
## Markup
|
|
161
73
|
|
|
162
|
-
|
|
163
|
-
- **Typesafe**: Full TypeScript/JSDoc support with autocomplete.
|
|
164
|
-
- **Variants**: Messages can have variants for pluralization, gender, etc.
|
|
165
|
-
- **Fallbacks**: Missing translations fall back to the base locale.
|
|
74
|
+
Messages can contain markup such as \`{#link to=|/docs|}Read the docs{/link}\`. Calling the message returns plain text. To render markup, use \`m.key.parts()\` or the \`<ParaglideMessage>\` component from \`@inlang/paraglide-js-react\`, \`-vue\`, \`-svelte\` or \`-solid\`. Tag names are app-defined, so \`{#b}\` does not render \`<b>\` on its own. See [markup](https://paraglidejs.com/markup).
|
|
166
75
|
|
|
167
76
|
## Links
|
|
168
77
|
|
|
169
|
-
- [Paraglide JS
|
|
170
|
-
- [Source Repository](https://github.com/opral/paraglide-js)
|
|
78
|
+
- [Paraglide JS documentation](https://paraglidejs.com) and [basics](https://paraglidejs.com/basics)
|
|
171
79
|
`;
|
|
172
80
|
}
|
|
173
81
|
//# sourceMappingURL=create-readme.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-readme.js","sourceRoot":"","sources":["../../src/compiler/create-readme.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"create-readme.js","sourceRoot":"","sources":["../../src/compiler/create-readme.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,IAI5B;IACA,MAAM,gBAAgB,GAAG,IAAI,CAAC,WAAW;QACxC,CAAC,CAAC,sBAAsB,IAAI,CAAC,WAAW,MAAM;QAC9C,CAAC,CAAC,EAAE,CAAC;IACN,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,kBAAkB,CAAC;IAC3D,MAAM,aAAa,GAClB,IAAI,CAAC,UAAU,KAAK,KAAK;QACxB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,qEAAqE,CAAC;IAE1E,OAAO;;;EAGN,gBAAgB;;;;;;;qGAOmF,WAAW;mCAC7E,WAAW;0FAC4C,WAAW;8KACyE,WAAW,6BAA6B,aAAa;;;;;;2PAMwB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4CrQ,CAAC;AACF,CAAC","sourcesContent":["/**\n * Creates the README.md content for the compiled output.\n *\n * The README tells coding agents what this generated folder is, what to edit\n * instead, and where inlang tooling ends and Paraglide JS begins.\n * See https://llmstxt.org/ for the format guidelines.\n */\nexport function createReadme(args: {\n\tprojectPath?: string;\n\t/** Whether the output also contains a `.gitignore` that ignores it. */\n\tgitignored?: boolean;\n}): string {\n\tconst compiledFromLine = args.projectPath\n\t\t? `\\nCompiled from: \\`${args.projectPath}\\`\\n`\n\t\t: \"\";\n\tconst projectPath = args.projectPath ?? \"./project.inlang\";\n\tconst gitignoreNote =\n\t\targs.gitignored === false\n\t\t\t? \"\"\n\t\t\t: \" This folder is git-ignored (see `.gitignore`), so don't commit it.\";\n\n\treturn `# Paraglide JS Compiled Output\n\n> Generated by [Paraglide JS](https://paraglidejs.com). Do not edit: every file here is overwritten on the next compile.\n${compiledFromLine}\n## What is this folder?\n\nParaglide JS is a compiler-based i18n library. It compiles an [inlang](https://inlang.com) project into typesafe message functions. Messages and params are type-checked, unused messages are tree-shaken out of the bundle, and the runtime includes locale detection and localized URLs. It targets Vite-based apps (React, TanStack Start/Router, SvelteKit, Vue, Solid, Astro, ...) and has plugins for other bundlers. It also compiles standalone with its CLI (\\`npx @inlang/paraglide-js compile\\`), without a bundler.\n\n## Edit the source, not this folder\n\n- **Text and translations:** the translation files of the inlang project (see \\`pathPattern\\` in \\`${projectPath}/settings.json\\`, for example \\`messages/{locale}.json\\`). Add, rename and fix messages there.\n- **Locales and file format:** \\`${projectPath}/settings.json\\`.\n- **Compiler options** (\\`strategy\\`, \\`outdir\\`, ...): the bundler plugin options or \\`${projectPath}/paraglide.config.*\\`.\n- **Regenerate:** the bundler plugin (for example \\`paraglideVitePlugin\\`) recompiles on dev and build. Without a bundler, run \\`npx @inlang/paraglide-js compile --project ${projectPath} --outdir <this folder>\\`.${gitignoreNote}\n\nIf a new message has no \\`m.*\\` function yet, the output is stale: recompile (for example, restart the dev server).\n\n## inlang vs. Paraglide JS\n\n- **inlang** owns the localization project: messages, translation files, \\`settings.json\\`, plugins, and the tooling around them. Use the inlang CLI for [checks](https://inlang.com/m/2qj2w8pu/app-inlang-cli#check) (\\`npx @inlang/cli check --project ${projectPath}\\` finds missing translations and unused messages) and [machine translation](https://inlang.com/m/2qj2w8pu/app-inlang-cli#machine-translate). Translators can use [Fink](https://inlang.com/m/tdozzpar/app-inlang-finkLocalizationEditor), designers [Parrot](https://inlang.com/m/gkrpgoir/app-parrot-figmaPlugin) in Figma, and developers [Sherlock](https://inlang.com/m/r7kp499g/app-inlang-ideExtension) in VS Code. The project folder has its own README.\n- **Paraglide JS** owns this folder: message functions, the locale runtime, and server middleware.\n- The file format (inlang message format, i18next, ICU MessageFormat, ...) is an inlang plugin. Switching formats doesn't change your \\`m.*\\` calls. See [file formats](https://paraglidejs.com/file-formats). Switching to a runtime library such as i18next gives up the compiled typesafety and tree-shaking; inlang's checks, machine translation and editors keep working through a format plugin. See the [comparison](https://paraglidejs.com/comparison).\n\n\\`\\`\\`\nparaglide/\n├── messages.js # Message exports (import this)\n├── messages/ # Individual message functions\n├── runtime.js # Locale detection & configuration\n├── registry.js # Formatters for plural, number, datetime, date, time, relativetime, icu:pound\n├── server.js # Server-side middleware\n└── .gitignore # Marks folder as generated\n\\`\\`\\`\n\n## Usage\n\n\\`\\`\\`js\nimport { m } from \"./paraglide/messages.js\";\n\nm.hello_world(); // \"Hello, World!\" (current locale)\nm.greeting({ name: \"Sam\" }); // \"Hello, Sam!\"\nm.greeting({ name: \"Sam\" }, { locale: \"de\" }); // \"Hallo, Sam!\"\n\\`\\`\\`\n\n\\`\\`\\`js\nimport { getLocale, setLocale, locales, baseLocale, getTextDirection } from \"./paraglide/runtime.js\";\n\ngetLocale(); // e.g. \"en\"\nsetLocale(\"de\"); // reloads the page by default; pass { reload: false } to skip\nlocales; // e.g. [\"en\", \"de\"]\ngetTextDirection(); // \"ltr\" | \"rtl\"\n\\`\\`\\`\n\n## Strategy and localized URLs\n\nThe compiler option \\`strategy\\` is an ordered list of how the locale is detected and stored; the first strategy that resolves a locale wins (default \\`[\"cookie\", \"globalVariable\", \"baseLocale\"]\\`). Options include \\`\"url\"\\` (localized URLs such as \\`/de/about\\`), \\`\"cookie\"\\`, \\`\"localStorage\"\\`, \\`\"preferredLanguage\"\\`, \\`\"globalVariable\"\\`, \\`\"baseLocale\"\\` and \\`\"custom-*\"\\`. See [strategy](https://paraglidejs.com/strategy) and [i18n routing](https://paraglidejs.com/i18n-routing).\n\n## Markup\n\nMessages can contain markup such as \\`{#link to=|/docs|}Read the docs{/link}\\`. Calling the message returns plain text. To render markup, use \\`m.key.parts()\\` or the \\`<ParaglideMessage>\\` component from \\`@inlang/paraglide-js-react\\`, \\`-vue\\`, \\`-svelte\\` or \\`-solid\\`. Tag names are app-defined, so \\`{#b}\\` does not render \\`<b>\\` on its own. See [markup](https://paraglidejs.com/markup).\n\n## Links\n\n- [Paraglide JS documentation](https://paraglidejs.com) and [basics](https://paraglidejs.com/basics)\n`;\n}\n"]}
|
|
@@ -33,13 +33,13 @@ test("emitTsDeclarations uses the compiler API for a default-only interop shape
|
|
|
33
33
|
messages: [
|
|
34
34
|
{
|
|
35
35
|
id: "greeting.hello_en",
|
|
36
|
-
|
|
36
|
+
bundle_id: "greeting.hello",
|
|
37
37
|
locale: "en",
|
|
38
38
|
selectors: [],
|
|
39
39
|
variants: [
|
|
40
40
|
{
|
|
41
41
|
id: "greeting.hello_en_variant",
|
|
42
|
-
|
|
42
|
+
message_id: "greeting.hello",
|
|
43
43
|
matches: [],
|
|
44
44
|
pattern: [{ type: "text", value: "Hello" }],
|
|
45
45
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emit-ts-declarations.interop.test.js","sourceRoot":"","sources":["../../src/compiler/emit-ts-declarations.interop.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAEN,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,GACV,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,oEAAoE;AACpE,wEAAwE;AACxE,sEAAsE;AACtE,sEAAsE;AACtE,yEAAyE;AACzE,2DAA2D;AAC3D,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,8CAA8C;AAC9C,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;IAChC,MAAM,MAAM,GACX,MAAM,EAAE,CAAC,YAAY,CAA8B,YAAY,CAAC,CAAC;IAClE,OAAO;QACN,OAAO,EAAE,MAAM;QACf,aAAa,EAAE,SAAS;KACxB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2FAA2F,EAAE,KAAK,IAAI,EAAE;IAC5G,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC;QACzC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,OAAO,EAAE,CAAC,IAAI,CAAC;gBACf,UAAU,EAAE,IAAI;aAChB;SACD,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,MAAM,GAAiB;QAC5B,EAAE,EAAE,gBAAgB;QACpB,YAAY,EAAE,EAAE;QAChB,QAAQ,EAAE;YACT;gBACC,EAAE,EAAE,mBAAmB;gBACvB,
|
|
1
|
+
{"version":3,"file":"emit-ts-declarations.interop.test.js","sourceRoot":"","sources":["../../src/compiler/emit-ts-declarations.interop.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAEN,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,GACV,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,oEAAoE;AACpE,wEAAwE;AACxE,sEAAsE;AACtE,sEAAsE;AACtE,yEAAyE;AACzE,2DAA2D;AAC3D,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,8CAA8C;AAC9C,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;IAChC,MAAM,MAAM,GACX,MAAM,EAAE,CAAC,YAAY,CAA8B,YAAY,CAAC,CAAC;IAClE,OAAO;QACN,OAAO,EAAE,MAAM;QACf,aAAa,EAAE,SAAS;KACxB,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2FAA2F,EAAE,KAAK,IAAI,EAAE;IAC5G,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC;QACzC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,OAAO,EAAE,CAAC,IAAI,CAAC;gBACf,UAAU,EAAE,IAAI;aAChB;SACD,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,MAAM,GAAiB;QAC5B,EAAE,EAAE,gBAAgB;QACpB,YAAY,EAAE,EAAE;QAChB,QAAQ,EAAE;YACT;gBACC,EAAE,EAAE,mBAAmB;gBACvB,SAAS,EAAE,gBAAgB;gBAC3B,MAAM,EAAE,IAAI;gBACZ,SAAS,EAAE,EAAE;gBACb,QAAQ,EAAE;oBACT;wBACC,EAAE,EAAE,2BAA2B;wBAC/B,UAAU,EAAE,gBAAgB;wBAC5B,OAAO,EAAE,EAAE;wBACX,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;qBAC3C;iBACD;aACD;SACD;KACD,CAAC;IACF,MAAM,kBAAkB,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAE7C,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC;QACnC,OAAO;QACP,eAAe,EAAE;YAChB,kBAAkB,EAAE,IAAI;SACxB;KACD,CAAC,CAAC;IAEH,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;IAC9C,yEAAyE;IACzE,yEAAyE;IACzE,MAAM,CAAC,MAAM,CAAC,8BAA8B,CAAC,CAAC,CAAC,SAAS,CACvD,gDAAgD,CAChD,CAAC;AACH,CAAC,CAAC,CAAC","sourcesContent":["import { expect, test, vi } from \"vitest\";\nimport {\n\ttype BundleNested,\n\tinsertBundleNested,\n\tloadProjectInMemory,\n\tnewProject,\n} from \"@inlang/sdk\";\nimport { compileProject } from \"./compile-project.js\";\n\n// Bundlers and CJS interop can deliver the `typescript` module in a\n// default-only shape (`{ default: ts }` without hoisted named exports).\n// TypeScript 5/6 in that shape must still be routed to the in-process\n// compiler API: the CLI fallback intended for TypeScript 7+ would run\n// TypeScript 5's `tsc`, whose declaration emitter produces syntactically\n// invalid unquoted export aliases (`export { x as a.b }`).\n//\n// `createProgram: undefined` is stubbed explicitly because vitest wraps\n// factory mocks in a proxy that throws on missing exports, while a real\n// default-only namespace returns `undefined`.\nvi.mock(\"typescript\", async () => {\n\tconst actual =\n\t\tawait vi.importActual<typeof import(\"typescript\")>(\"typescript\");\n\treturn {\n\t\tdefault: actual,\n\t\tcreateProgram: undefined,\n\t};\n});\n\ntest(\"emitTsDeclarations uses the compiler API for a default-only interop shape of TypeScript 5\", async () => {\n\tconst project = await loadProjectInMemory({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tlocales: [\"en\"],\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t},\n\t\t}),\n\t});\n\n\tconst bundle: BundleNested = {\n\t\tid: \"greeting.hello\",\n\t\tdeclarations: [],\n\t\tmessages: [\n\t\t\t{\n\t\t\t\tid: \"greeting.hello_en\",\n\t\t\t\tbundle_id: \"greeting.hello\",\n\t\t\t\tlocale: \"en\",\n\t\t\t\tselectors: [],\n\t\t\t\tvariants: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"greeting.hello_en_variant\",\n\t\t\t\t\t\tmessage_id: \"greeting.hello\",\n\t\t\t\t\t\tmatches: [],\n\t\t\t\t\t\tpattern: [{ type: \"text\", value: \"Hello\" }],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\tawait insertBundleNested(project.db, bundle);\n\n\tconst output = await compileProject({\n\t\tproject,\n\t\tcompilerOptions: {\n\t\t\temitTsDeclarations: true,\n\t\t},\n\t});\n\n\texpect(output).toHaveProperty(\"runtime.d.ts\");\n\t// the quoted alias distinguishes the two paths: TypeScript 5's CLI would\n\t// emit the invalid `export { greeting_hello as greeting.hello }` instead\n\texpect(output[\"messages/greeting_hello.d.ts\"]).toContain(\n\t\t`export { greeting_hello as \"greeting.hello\" };`\n\t);\n});\n"]}
|
|
@@ -59,13 +59,13 @@ async function newProjectWithMessages() {
|
|
|
59
59
|
messages: [
|
|
60
60
|
{
|
|
61
61
|
id: "greeting.hello_en",
|
|
62
|
-
|
|
62
|
+
bundle_id: "greeting.hello",
|
|
63
63
|
locale: "en",
|
|
64
64
|
selectors: [],
|
|
65
65
|
variants: [
|
|
66
66
|
{
|
|
67
67
|
id: "greeting.hello_en_variant",
|
|
68
|
-
|
|
68
|
+
message_id: "greeting.hello",
|
|
69
69
|
matches: [],
|
|
70
70
|
pattern: [{ type: "text", value: "Hello" }],
|
|
71
71
|
},
|
|
@@ -80,13 +80,13 @@ async function newProjectWithMessages() {
|
|
|
80
80
|
messages: [
|
|
81
81
|
{
|
|
82
82
|
id: "balance_en",
|
|
83
|
-
|
|
83
|
+
bundle_id: "balance",
|
|
84
84
|
locale: "en",
|
|
85
85
|
selectors: [],
|
|
86
86
|
variants: [
|
|
87
87
|
{
|
|
88
88
|
id: "balance_en_variant",
|
|
89
|
-
|
|
89
|
+
message_id: "balance",
|
|
90
90
|
matches: [],
|
|
91
91
|
pattern: [
|
|
92
92
|
{ type: "text", value: "You have " },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emit-ts-declarations.test.js","sourceRoot":"","sources":["../../src/compiler/emit-ts-declarations.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { beforeEach, expect, test, vi } from \"vitest\";\nimport { createProject as typescriptProject, ts } from \"@ts-morph/bootstrap\";\nimport {\n\ttype BundleNested,\n\tinsertBundleNested,\n\tloadProjectInMemory,\n\tnewProject,\n} from \"@inlang/sdk\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { compileProject } from \"./compile-project.js\";\n\n// TypeScript 7 (the Go-based compiler, npm `latest` since 7.0.2) no longer\n// ships the in-process compiler API. Its main entry point only exports\n// `version` and `versionMajorMinor` — no `createProgram`, no `ScriptTarget`,\n// no `ModuleKind`. Emitting declarations must go through the `tsc` CLI\n// instead. https://github.com/opral/paraglide-js/issues/711\n//\n// The repo's own `typescript` dev dependency stays on 5.x for the existing\n// tests; this file swaps in the aliased TypeScript 7 package to simulate a\n// user who upgraded.\n//\n// Note: vitest wraps factory mocks in a proxy that throws on missing exports.\n// The real TypeScript 7 module returns `undefined` for them instead, so the\n// classic compiler API surface is stubbed out explicitly to mirror that.\nvi.mock(\"typescript\", async () => {\n\tconst actual =\n\t\tawait vi.importActual<typeof import(\"typescript-go\")>(\"typescript-go\");\n\treturn {\n\t\t...actual,\n\t\tcreateProgram: undefined,\n\t\tcreateCompilerHost: undefined,\n\t\tcreateSourceFile: undefined,\n\t\tScriptTarget: undefined,\n\t\tScriptKind: undefined,\n\t\tModuleKind: undefined,\n\t\tModuleResolutionKind: undefined,\n\t};\n});\n\n// In production, the `typescript` module and the `tsc` CLI resolve to the\n// same package. In this test the module is mocked with the TypeScript 7\n// alias, so the CLI resolution must point at that alias too — otherwise it\n// would find the repo's TypeScript 5 dev dependency. Mutable so the error\n// path can be tested with a broken resolution.\nconst tscResolution = vi.hoisted(() => ({ tscJsPath: \"\" }));\n\nvi.mock(\"./resolve-tsc-js-path.js\", () => ({\n\tresolveTscJsPath: () => tscResolution.tscJsPath,\n}));\n\nconst typescriptGoTscJsPath = path.join(\n\tpath.dirname(\n\t\tcreateRequire(import.meta.url).resolve(\"typescript-go/package.json\")\n\t),\n\t\"lib\",\n\t\"tsc.js\"\n);\n\nbeforeEach(() => {\n\ttscResolution.tscJsPath = typescriptGoTscJsPath;\n});\n\nasync function newProjectWithMessages() {\n\tconst project = await loadProjectInMemory({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tlocales: [\"en\"],\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t},\n\t\t}),\n\t});\n\n\t// a message id that is not a valid JS identifier -> quoted export alias\n\tconst quotedAliasBundle: BundleNested = {\n\t\tid: \"greeting.hello\",\n\t\tdeclarations: [],\n\t\tmessages: [\n\t\t\t{\n\t\t\t\tid: \"greeting.hello_en\",\n\t\t\t\tbundleId: \"greeting.hello\",\n\t\t\t\tlocale: \"en\",\n\t\t\t\tselectors: [],\n\t\t\t\tvariants: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"greeting.hello_en_variant\",\n\t\t\t\t\t\tmessageId: \"greeting.hello\",\n\t\t\t\t\t\tmatches: [],\n\t\t\t\t\t\tpattern: [{ type: \"text\", value: \"Hello\" }],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\t// a message with an input so the declarations carry a typed parameter\n\tconst parameterizedBundle: BundleNested = {\n\t\tid: \"balance\",\n\t\tdeclarations: [{ type: \"input-variable\", name: \"amount\" }],\n\t\tmessages: [\n\t\t\t{\n\t\t\t\tid: \"balance_en\",\n\t\t\t\tbundleId: \"balance\",\n\t\t\t\tlocale: \"en\",\n\t\t\t\tselectors: [],\n\t\t\t\tvariants: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"balance_en_variant\",\n\t\t\t\t\t\tmessageId: \"balance\",\n\t\t\t\t\t\tmatches: [],\n\t\t\t\t\t\tpattern: [\n\t\t\t\t\t\t\t{ type: \"text\", value: \"You have \" },\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"expression\",\n\t\t\t\t\t\t\t\targ: { type: \"variable-reference\", name: \"amount\" },\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ type: \"text\", value: \" coins.\" },\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tawait insertBundleNested(project.db, quotedAliasBundle);\n\tawait insertBundleNested(project.db, parameterizedBundle);\n\treturn project;\n}\n\ntest(\"emitTsDeclarations works with TypeScript 7 (tsgo)\", async () => {\n\tconst project = await newProjectWithMessages();\n\n\tfor (const outputStructure of [\n\t\t\"message-modules\",\n\t\t\"locale-modules\",\n\t] as const) {\n\t\tconst output = await compileProject({\n\t\t\tproject,\n\t\t\tcompilerOptions: {\n\t\t\t\temitTsDeclarations: true,\n\t\t\t\toutputStructure,\n\t\t\t\t// path-traversing keys (incl. Windows separators) must be\n\t\t\t\t// silently skipped like on the compiler-API path — neither\n\t\t\t\t// escaping the temp dir nor failing the completeness check\n\t\t\t\tadditionalFiles: {\n\t\t\t\t\t\"../escape.js\": \"export const escape = 1;\",\n\t\t\t\t\t\"foo\\\\..\\\\..\\\\escape.js\": \"export const escape = 1;\",\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\n\t\texpect(output[\"../escape.d.ts\"]).toBeUndefined();\n\t\texpect(output[\"foo\\\\..\\\\..\\\\escape.d.ts\"]).toBeUndefined();\n\n\t\texpect(output).toHaveProperty(\"runtime.d.ts\");\n\t\texpect(output).toHaveProperty(\"messages.d.ts\");\n\t\texpect(output).toHaveProperty(\"registry.d.ts\");\n\t\texpect(output[\"runtime.d.ts\"]).toContain(\"getLocale\");\n\n\t\t// TypeScript 5's declaration emitter drops the quotes around export\n\t\t// aliases that are not valid identifiers (worked around by a custom\n\t\t// transformer on the compiler-API path); tsgo emits them correctly.\n\t\tconst declarationFile =\n\t\t\toutputStructure === \"message-modules\"\n\t\t\t\t? \"messages/greeting_hello.d.ts\"\n\t\t\t\t: \"messages/_index.d.ts\";\n\n\t\texpect(output[declarationFile]).toContain(`as \"greeting.hello\"`);\n\n\t\t// the emitted declarations must typecheck for a consumer\n\t\t// (@ts-morph/bootstrap bundles its own compiler and is unaffected\n\t\t// by the TypeScript 7 mock above)\n\t\tconst tsProject = await typescriptProject({\n\t\t\tuseInMemoryFileSystem: true,\n\t\t\tcompilerOptions: {\n\t\t\t\tmodule: ts.ModuleKind.Node16,\n\t\t\t\tmoduleResolution: ts.ModuleResolutionKind.Node16,\n\t\t\t\tstrict: true,\n\t\t\t},\n\t\t});\n\n\t\tfor (const [fileName, code] of Object.entries(output)) {\n\t\t\tif (fileName.endsWith(\".d.ts\")) {\n\t\t\t\ttsProject.createSourceFile(fileName, code);\n\t\t\t}\n\t\t}\n\n\t\ttsProject.createSourceFile(\n\t\t\t\"test.ts\",\n\t\t\t`\n\t\t\t\timport { \"greeting.hello\" as greetingHello, balance } from \"./messages.js\";\n\n\t\t\t\tgreetingHello() satisfies string;\n\t\t\t\tbalance({ amount: 5 }) satisfies string;\n\t\t\t`\n\t\t);\n\n\t\tconst program = tsProject.createProgram();\n\t\tconst diagnostics = ts.getPreEmitDiagnostics(program);\n\t\tfor (const diagnostic of diagnostics) {\n\t\t\tconsole.error(diagnostic.messageText, diagnostic.file?.fileName);\n\t\t}\n\t\texpect(diagnostics.length).toEqual(0);\n\t}\n});\n\ntest(\"emitTsDeclarations throws a descriptive error when the tsc CLI produces no declarations\", async () => {\n\ttscResolution.tscJsPath = path.join(\n\t\tpath.dirname(typescriptGoTscJsPath),\n\t\t\"does-not-exist.js\"\n\t);\n\n\tconst project = await newProjectWithMessages();\n\n\tawait expect(\n\t\tcompileProject({\n\t\t\tproject,\n\t\t\tcompilerOptions: {\n\t\t\t\temitTsDeclarations: true,\n\t\t\t},\n\t\t})\n\t).rejects.toThrow(/did not produce a declaration file for every compiled/);\n});\n"]}
|
|
1
|
+
{"version":3,"file":"emit-ts-declarations.test.js","sourceRoot":"","sources":["../../src/compiler/emit-ts-declarations.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { beforeEach, expect, test, vi } from \"vitest\";\nimport { createProject as typescriptProject, ts } from \"@ts-morph/bootstrap\";\nimport {\n\ttype BundleNested,\n\tinsertBundleNested,\n\tloadProjectInMemory,\n\tnewProject,\n} from \"@inlang/sdk\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { compileProject } from \"./compile-project.js\";\n\n// TypeScript 7 (the Go-based compiler, npm `latest` since 7.0.2) no longer\n// ships the in-process compiler API. Its main entry point only exports\n// `version` and `versionMajorMinor` — no `createProgram`, no `ScriptTarget`,\n// no `ModuleKind`. Emitting declarations must go through the `tsc` CLI\n// instead. https://github.com/opral/paraglide-js/issues/711\n//\n// The repo's own `typescript` dev dependency stays on 5.x for the existing\n// tests; this file swaps in the aliased TypeScript 7 package to simulate a\n// user who upgraded.\n//\n// Note: vitest wraps factory mocks in a proxy that throws on missing exports.\n// The real TypeScript 7 module returns `undefined` for them instead, so the\n// classic compiler API surface is stubbed out explicitly to mirror that.\nvi.mock(\"typescript\", async () => {\n\tconst actual =\n\t\tawait vi.importActual<typeof import(\"typescript-go\")>(\"typescript-go\");\n\treturn {\n\t\t...actual,\n\t\tcreateProgram: undefined,\n\t\tcreateCompilerHost: undefined,\n\t\tcreateSourceFile: undefined,\n\t\tScriptTarget: undefined,\n\t\tScriptKind: undefined,\n\t\tModuleKind: undefined,\n\t\tModuleResolutionKind: undefined,\n\t};\n});\n\n// In production, the `typescript` module and the `tsc` CLI resolve to the\n// same package. In this test the module is mocked with the TypeScript 7\n// alias, so the CLI resolution must point at that alias too — otherwise it\n// would find the repo's TypeScript 5 dev dependency. Mutable so the error\n// path can be tested with a broken resolution.\nconst tscResolution = vi.hoisted(() => ({ tscJsPath: \"\" }));\n\nvi.mock(\"./resolve-tsc-js-path.js\", () => ({\n\tresolveTscJsPath: () => tscResolution.tscJsPath,\n}));\n\nconst typescriptGoTscJsPath = path.join(\n\tpath.dirname(\n\t\tcreateRequire(import.meta.url).resolve(\"typescript-go/package.json\")\n\t),\n\t\"lib\",\n\t\"tsc.js\"\n);\n\nbeforeEach(() => {\n\ttscResolution.tscJsPath = typescriptGoTscJsPath;\n});\n\nasync function newProjectWithMessages() {\n\tconst project = await loadProjectInMemory({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tlocales: [\"en\"],\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t},\n\t\t}),\n\t});\n\n\t// a message id that is not a valid JS identifier -> quoted export alias\n\tconst quotedAliasBundle: BundleNested = {\n\t\tid: \"greeting.hello\",\n\t\tdeclarations: [],\n\t\tmessages: [\n\t\t\t{\n\t\t\t\tid: \"greeting.hello_en\",\n\t\t\t\tbundle_id: \"greeting.hello\",\n\t\t\t\tlocale: \"en\",\n\t\t\t\tselectors: [],\n\t\t\t\tvariants: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"greeting.hello_en_variant\",\n\t\t\t\t\t\tmessage_id: \"greeting.hello\",\n\t\t\t\t\t\tmatches: [],\n\t\t\t\t\t\tpattern: [{ type: \"text\", value: \"Hello\" }],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\t// a message with an input so the declarations carry a typed parameter\n\tconst parameterizedBundle: BundleNested = {\n\t\tid: \"balance\",\n\t\tdeclarations: [{ type: \"input-variable\", name: \"amount\" }],\n\t\tmessages: [\n\t\t\t{\n\t\t\t\tid: \"balance_en\",\n\t\t\t\tbundle_id: \"balance\",\n\t\t\t\tlocale: \"en\",\n\t\t\t\tselectors: [],\n\t\t\t\tvariants: [\n\t\t\t\t\t{\n\t\t\t\t\t\tid: \"balance_en_variant\",\n\t\t\t\t\t\tmessage_id: \"balance\",\n\t\t\t\t\t\tmatches: [],\n\t\t\t\t\t\tpattern: [\n\t\t\t\t\t\t\t{ type: \"text\", value: \"You have \" },\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"expression\",\n\t\t\t\t\t\t\t\targ: { type: \"variable-reference\", name: \"amount\" },\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{ type: \"text\", value: \" coins.\" },\n\t\t\t\t\t\t],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tawait insertBundleNested(project.db, quotedAliasBundle);\n\tawait insertBundleNested(project.db, parameterizedBundle);\n\treturn project;\n}\n\ntest(\"emitTsDeclarations works with TypeScript 7 (tsgo)\", async () => {\n\tconst project = await newProjectWithMessages();\n\n\tfor (const outputStructure of [\n\t\t\"message-modules\",\n\t\t\"locale-modules\",\n\t] as const) {\n\t\tconst output = await compileProject({\n\t\t\tproject,\n\t\t\tcompilerOptions: {\n\t\t\t\temitTsDeclarations: true,\n\t\t\t\toutputStructure,\n\t\t\t\t// path-traversing keys (incl. Windows separators) must be\n\t\t\t\t// silently skipped like on the compiler-API path — neither\n\t\t\t\t// escaping the temp dir nor failing the completeness check\n\t\t\t\tadditionalFiles: {\n\t\t\t\t\t\"../escape.js\": \"export const escape = 1;\",\n\t\t\t\t\t\"foo\\\\..\\\\..\\\\escape.js\": \"export const escape = 1;\",\n\t\t\t\t},\n\t\t\t},\n\t\t});\n\n\t\texpect(output[\"../escape.d.ts\"]).toBeUndefined();\n\t\texpect(output[\"foo\\\\..\\\\..\\\\escape.d.ts\"]).toBeUndefined();\n\n\t\texpect(output).toHaveProperty(\"runtime.d.ts\");\n\t\texpect(output).toHaveProperty(\"messages.d.ts\");\n\t\texpect(output).toHaveProperty(\"registry.d.ts\");\n\t\texpect(output[\"runtime.d.ts\"]).toContain(\"getLocale\");\n\n\t\t// TypeScript 5's declaration emitter drops the quotes around export\n\t\t// aliases that are not valid identifiers (worked around by a custom\n\t\t// transformer on the compiler-API path); tsgo emits them correctly.\n\t\tconst declarationFile =\n\t\t\toutputStructure === \"message-modules\"\n\t\t\t\t? \"messages/greeting_hello.d.ts\"\n\t\t\t\t: \"messages/_index.d.ts\";\n\n\t\texpect(output[declarationFile]).toContain(`as \"greeting.hello\"`);\n\n\t\t// the emitted declarations must typecheck for a consumer\n\t\t// (@ts-morph/bootstrap bundles its own compiler and is unaffected\n\t\t// by the TypeScript 7 mock above)\n\t\tconst tsProject = await typescriptProject({\n\t\t\tuseInMemoryFileSystem: true,\n\t\t\tcompilerOptions: {\n\t\t\t\tmodule: ts.ModuleKind.Node16,\n\t\t\t\tmoduleResolution: ts.ModuleResolutionKind.Node16,\n\t\t\t\tstrict: true,\n\t\t\t},\n\t\t});\n\n\t\tfor (const [fileName, code] of Object.entries(output)) {\n\t\t\tif (fileName.endsWith(\".d.ts\")) {\n\t\t\t\ttsProject.createSourceFile(fileName, code);\n\t\t\t}\n\t\t}\n\n\t\ttsProject.createSourceFile(\n\t\t\t\"test.ts\",\n\t\t\t`\n\t\t\t\timport { \"greeting.hello\" as greetingHello, balance } from \"./messages.js\";\n\n\t\t\t\tgreetingHello() satisfies string;\n\t\t\t\tbalance({ amount: 5 }) satisfies string;\n\t\t\t`\n\t\t);\n\n\t\tconst program = tsProject.createProgram();\n\t\tconst diagnostics = ts.getPreEmitDiagnostics(program);\n\t\tfor (const diagnostic of diagnostics) {\n\t\t\tconsole.error(diagnostic.messageText, diagnostic.file?.fileName);\n\t\t}\n\t\texpect(diagnostics.length).toEqual(0);\n\t}\n});\n\ntest(\"emitTsDeclarations throws a descriptive error when the tsc CLI produces no declarations\", async () => {\n\ttscResolution.tscJsPath = path.join(\n\t\tpath.dirname(typescriptGoTscJsPath),\n\t\t\"does-not-exist.js\"\n\t);\n\n\tconst project = await newProjectWithMessages();\n\n\tawait expect(\n\t\tcompileProject({\n\t\t\tproject,\n\t\t\tcompilerOptions: {\n\t\t\t\temitTsDeclarations: true,\n\t\t\t},\n\t\t})\n\t).rejects.toThrow(/did not produce a declaration file for every compiled/);\n});\n"]}
|
package/dist/compiler/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export type { ServerRuntime } from "./server/type.js";
|
|
|
5
5
|
export { compile } from "./compile.js";
|
|
6
6
|
export { compileProject } from "./compile-project.js";
|
|
7
7
|
export { compileBundle } from "./compile-bundle.js";
|
|
8
|
-
export { compileMessage } from "./compile-message.js";
|
|
8
|
+
export { compileMessage, type CompilableMessage, type CompilableVariant, } from "./compile-message.js";
|
|
9
9
|
export { compilePattern } from "./compile-pattern.js";
|
|
10
10
|
export { writeOutput } from "../services/file-handling/write-output.js";
|
|
11
11
|
export { createParaglide } from "./create-paraglide.js";
|
package/dist/compiler/index.js
CHANGED
|
@@ -2,7 +2,7 @@ export { defaultCompilerOptions, } from "./compiler-options.js";
|
|
|
2
2
|
export { compile } from "./compile.js";
|
|
3
3
|
export { compileProject } from "./compile-project.js";
|
|
4
4
|
export { compileBundle } from "./compile-bundle.js";
|
|
5
|
-
export { compileMessage } from "./compile-message.js";
|
|
5
|
+
export { compileMessage, } from "./compile-message.js";
|
|
6
6
|
export { compilePattern } from "./compile-pattern.js";
|
|
7
7
|
export { writeOutput } from "../services/file-handling/write-output.js";
|
|
8
8
|
export { createParaglide } from "./create-paraglide.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compiler/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,sBAAsB,GAEtB,MAAM,uBAAuB,CAAC;AAgB/B,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compiler/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,sBAAsB,GAEtB,MAAM,uBAAuB,CAAC;AAgB/B,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EACN,cAAc,GAGd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC","sourcesContent":["export {\n\tdefaultCompilerOptions,\n\ttype CompilerOptions,\n} from \"./compiler-options.js\";\nexport type {\n\tLocalizedString,\n\tMessagePart,\n\tMessageMarkupAttribute,\n\tMessageMarkupOption,\n\tMessageMarkupAttributes,\n\tMessageMarkupOptions,\n\tMessageMarkupTag,\n\tMessageMarkupSchema,\n\tMessageMetadata,\n\tMessageBundleFunction,\n\tMessageFunction,\n} from \"./types.js\";\nexport type { Runtime } from \"./runtime/type.js\";\nexport type { ServerRuntime } from \"./server/type.js\";\nexport { compile } from \"./compile.js\";\nexport { compileProject } from \"./compile-project.js\";\nexport { compileBundle } from \"./compile-bundle.js\";\nexport {\n\tcompileMessage,\n\ttype CompilableMessage,\n\ttype CompilableVariant,\n} from \"./compile-message.js\";\nexport { compilePattern } from \"./compile-pattern.js\";\nexport { writeOutput } from \"../services/file-handling/write-output.js\";\nexport { createParaglide } from \"./create-paraglide.js\";\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Declaration } from "@inlang/sdk";
|
|
2
|
+
/**
|
|
3
|
+
* Returns the name of the input a variable holds the unmodified value of, or
|
|
4
|
+
* `undefined` if the variable can hold anything else.
|
|
5
|
+
*
|
|
6
|
+
* Inputs resolve to themselves. A local variable resolves to an input when it
|
|
7
|
+
* is an un-annotated reference to an input, directly or through a chain of
|
|
8
|
+
* un-annotated local variables. Annotated locals (`:plural`, `:number`, ...)
|
|
9
|
+
* and literal values do not resolve, because their value is not the input.
|
|
10
|
+
*
|
|
11
|
+
* Message selectors use this to match aliases like the ones ICU MessageFormat 1
|
|
12
|
+
* imports produce for exact number cases (`=0`) the same way as the input:
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // .input {$count}
|
|
16
|
+
* // .local countPluralExact = {$count}
|
|
17
|
+
* // .local countPlural = {$count :plural}
|
|
18
|
+
* resolveInputAlias("countPluralExact", declarations) // "count"
|
|
19
|
+
* resolveInputAlias("countPlural", declarations) // undefined
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveInputAlias(name: string, declarations: readonly Declaration[]): string | undefined;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the name of the input a variable holds the unmodified value of, or
|
|
3
|
+
* `undefined` if the variable can hold anything else.
|
|
4
|
+
*
|
|
5
|
+
* Inputs resolve to themselves. A local variable resolves to an input when it
|
|
6
|
+
* is an un-annotated reference to an input, directly or through a chain of
|
|
7
|
+
* un-annotated local variables. Annotated locals (`:plural`, `:number`, ...)
|
|
8
|
+
* and literal values do not resolve, because their value is not the input.
|
|
9
|
+
*
|
|
10
|
+
* Message selectors use this to match aliases like the ones ICU MessageFormat 1
|
|
11
|
+
* imports produce for exact number cases (`=0`) the same way as the input:
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // .input {$count}
|
|
15
|
+
* // .local countPluralExact = {$count}
|
|
16
|
+
* // .local countPlural = {$count :plural}
|
|
17
|
+
* resolveInputAlias("countPluralExact", declarations) // "count"
|
|
18
|
+
* resolveInputAlias("countPlural", declarations) // undefined
|
|
19
|
+
*/
|
|
20
|
+
export function resolveInputAlias(name, declarations) {
|
|
21
|
+
const visited = new Set();
|
|
22
|
+
let current = name;
|
|
23
|
+
while (!visited.has(current)) {
|
|
24
|
+
visited.add(current);
|
|
25
|
+
// first declaration wins, consistent with compileVariableAccess()
|
|
26
|
+
const declaration = declarations.find((candidate) => candidate.name === current);
|
|
27
|
+
if (!declaration) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
if (declaration.type === "input-variable") {
|
|
31
|
+
return declaration.name;
|
|
32
|
+
}
|
|
33
|
+
if (declaration.value.annotation ||
|
|
34
|
+
declaration.value.arg.type !== "variable-reference") {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
current = declaration.value.arg.name;
|
|
38
|
+
}
|
|
39
|
+
// cyclic local variable references
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=input-alias.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-alias.js","sourceRoot":"","sources":["../../src/compiler/input-alias.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,iBAAiB,CAChC,IAAY,EACZ,YAAoC;IAEpC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,IAAI,OAAO,GAAG,IAAI,CAAC;IAEnB,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrB,kEAAkE;QAClE,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CACpC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,OAAO,CACzC,CAAC;QACF,IAAI,CAAC,WAAW,EAAE,CAAC;YAClB,OAAO,SAAS,CAAC;QAClB,CAAC;QACD,IAAI,WAAW,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;YAC3C,OAAO,WAAW,CAAC,IAAI,CAAC;QACzB,CAAC;QACD,IACC,WAAW,CAAC,KAAK,CAAC,UAAU;YAC5B,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,KAAK,oBAAoB,EAClD,CAAC;YACF,OAAO,SAAS,CAAC;QAClB,CAAC;QACD,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;IACtC,CAAC;IAED,mCAAmC;IACnC,OAAO,SAAS,CAAC;AAClB,CAAC","sourcesContent":["import type { Declaration } from \"@inlang/sdk\";\n\n/**\n * Returns the name of the input a variable holds the unmodified value of, or\n * `undefined` if the variable can hold anything else.\n *\n * Inputs resolve to themselves. A local variable resolves to an input when it\n * is an un-annotated reference to an input, directly or through a chain of\n * un-annotated local variables. Annotated locals (`:plural`, `:number`, ...)\n * and literal values do not resolve, because their value is not the input.\n *\n * Message selectors use this to match aliases like the ones ICU MessageFormat 1\n * imports produce for exact number cases (`=0`) the same way as the input:\n *\n * @example\n * // .input {$count}\n * // .local countPluralExact = {$count}\n * // .local countPlural = {$count :plural}\n * resolveInputAlias(\"countPluralExact\", declarations) // \"count\"\n * resolveInputAlias(\"countPlural\", declarations) // undefined\n */\nexport function resolveInputAlias(\n\tname: string,\n\tdeclarations: readonly Declaration[]\n): string | undefined {\n\tconst visited = new Set<string>();\n\tlet current = name;\n\n\twhile (!visited.has(current)) {\n\t\tvisited.add(current);\n\t\t// first declaration wins, consistent with compileVariableAccess()\n\t\tconst declaration = declarations.find(\n\t\t\t(candidate) => candidate.name === current\n\t\t);\n\t\tif (!declaration) {\n\t\t\treturn undefined;\n\t\t}\n\t\tif (declaration.type === \"input-variable\") {\n\t\t\treturn declaration.name;\n\t\t}\n\t\tif (\n\t\t\tdeclaration.value.annotation ||\n\t\t\tdeclaration.value.arg.type !== \"variable-reference\"\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\tcurrent = declaration.value.arg.name;\n\t}\n\n\t// cyclic local variable references\n\treturn undefined;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import { resolveInputAlias } from "./input-alias.js";
|
|
3
|
+
const alias = (name, target) => ({
|
|
4
|
+
type: "local-variable",
|
|
5
|
+
name,
|
|
6
|
+
value: {
|
|
7
|
+
type: "expression",
|
|
8
|
+
arg: { type: "variable-reference", name: target },
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
test("resolves inputs and chains of un-annotated aliases to the input", () => {
|
|
12
|
+
const declarations = [
|
|
13
|
+
{ type: "input-variable", name: "count" },
|
|
14
|
+
alias("a", "count"),
|
|
15
|
+
alias("b", "a"),
|
|
16
|
+
];
|
|
17
|
+
expect(resolveInputAlias("count", declarations)).toBe("count");
|
|
18
|
+
expect(resolveInputAlias("a", declarations)).toBe("count");
|
|
19
|
+
expect(resolveInputAlias("b", declarations)).toBe("count");
|
|
20
|
+
});
|
|
21
|
+
test("does not resolve annotated locals, literals, aliases of them, or undeclared names", () => {
|
|
22
|
+
const declarations = [
|
|
23
|
+
{ type: "input-variable", name: "count" },
|
|
24
|
+
{
|
|
25
|
+
type: "local-variable",
|
|
26
|
+
name: "countPlural",
|
|
27
|
+
value: {
|
|
28
|
+
type: "expression",
|
|
29
|
+
arg: { type: "variable-reference", name: "count" },
|
|
30
|
+
annotation: { type: "function-reference", name: "plural", options: [] },
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
alias("pluralAlias", "countPlural"),
|
|
34
|
+
{
|
|
35
|
+
type: "local-variable",
|
|
36
|
+
name: "constant",
|
|
37
|
+
value: { type: "expression", arg: { type: "literal", value: "0" } },
|
|
38
|
+
},
|
|
39
|
+
alias("undeclaredAlias", "missing"),
|
|
40
|
+
];
|
|
41
|
+
expect(resolveInputAlias("countPlural", declarations)).toBeUndefined();
|
|
42
|
+
expect(resolveInputAlias("pluralAlias", declarations)).toBeUndefined();
|
|
43
|
+
expect(resolveInputAlias("constant", declarations)).toBeUndefined();
|
|
44
|
+
expect(resolveInputAlias("undeclaredAlias", declarations)).toBeUndefined();
|
|
45
|
+
expect(resolveInputAlias("missing", declarations)).toBeUndefined();
|
|
46
|
+
});
|
|
47
|
+
test("terminates on cyclic aliases", () => {
|
|
48
|
+
const declarations = [
|
|
49
|
+
alias("self", "self"),
|
|
50
|
+
alias("a", "b"),
|
|
51
|
+
alias("b", "c"),
|
|
52
|
+
alias("c", "a"),
|
|
53
|
+
];
|
|
54
|
+
expect(resolveInputAlias("self", declarations)).toBeUndefined();
|
|
55
|
+
expect(resolveInputAlias("a", declarations)).toBeUndefined();
|
|
56
|
+
expect(resolveInputAlias("c", declarations)).toBeUndefined();
|
|
57
|
+
});
|
|
58
|
+
//# sourceMappingURL=input-alias.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-alias.test.js","sourceRoot":"","sources":["../../src/compiler/input-alias.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,MAAc,EAAe,EAAE,CAAC,CAAC;IAC7D,IAAI,EAAE,gBAAgB;IACtB,IAAI;IACJ,KAAK,EAAE;QACN,IAAI,EAAE,YAAY;QAClB,GAAG,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,MAAM,EAAE;KACjD;CACD,CAAC,CAAC;AAEH,IAAI,CAAC,iEAAiE,EAAE,GAAG,EAAE;IAC5E,MAAM,YAAY,GAAkB;QACnC,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,OAAO,EAAE;QACzC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC;QACnB,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC;KACf,CAAC;IAEF,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/D,MAAM,CAAC,iBAAiB,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3D,MAAM,CAAC,iBAAiB,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC5D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,mFAAmF,EAAE,GAAG,EAAE;IAC9F,MAAM,YAAY,GAAkB;QACnC,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,OAAO,EAAE;QACzC;YACC,IAAI,EAAE,gBAAgB;YACtB,IAAI,EAAE,aAAa;YACnB,KAAK,EAAE;gBACN,IAAI,EAAE,YAAY;gBAClB,GAAG,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,OAAO,EAAE;gBAClD,UAAU,EAAE,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE;aACvE;SACD;QACD,KAAK,CAAC,aAAa,EAAE,aAAa,CAAC;QACnC;YACC,IAAI,EAAE,gBAAgB;YACtB,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;SACnE;QACD,KAAK,CAAC,iBAAiB,EAAE,SAAS,CAAC;KACnC,CAAC;IAEF,MAAM,CAAC,iBAAiB,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACvE,MAAM,CAAC,iBAAiB,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACvE,MAAM,CAAC,iBAAiB,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACpE,MAAM,CAAC,iBAAiB,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC3E,MAAM,CAAC,iBAAiB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;AACpE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8BAA8B,EAAE,GAAG,EAAE;IACzC,MAAM,YAAY,GAAkB;QACnC,KAAK,CAAC,MAAM,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC;QACf,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC;QACf,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC;KACf,CAAC;IAEF,MAAM,CAAC,iBAAiB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAChE,MAAM,CAAC,iBAAiB,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC7D,MAAM,CAAC,iBAAiB,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;AAC9D,CAAC,CAAC,CAAC","sourcesContent":["import { expect, test } from \"vitest\";\nimport type { Declaration } from \"@inlang/sdk\";\nimport { resolveInputAlias } from \"./input-alias.js\";\n\nconst alias = (name: string, target: string): Declaration => ({\n\ttype: \"local-variable\",\n\tname,\n\tvalue: {\n\t\ttype: \"expression\",\n\t\targ: { type: \"variable-reference\", name: target },\n\t},\n});\n\ntest(\"resolves inputs and chains of un-annotated aliases to the input\", () => {\n\tconst declarations: Declaration[] = [\n\t\t{ type: \"input-variable\", name: \"count\" },\n\t\talias(\"a\", \"count\"),\n\t\talias(\"b\", \"a\"),\n\t];\n\n\texpect(resolveInputAlias(\"count\", declarations)).toBe(\"count\");\n\texpect(resolveInputAlias(\"a\", declarations)).toBe(\"count\");\n\texpect(resolveInputAlias(\"b\", declarations)).toBe(\"count\");\n});\n\ntest(\"does not resolve annotated locals, literals, aliases of them, or undeclared names\", () => {\n\tconst declarations: Declaration[] = [\n\t\t{ type: \"input-variable\", name: \"count\" },\n\t\t{\n\t\t\ttype: \"local-variable\",\n\t\t\tname: \"countPlural\",\n\t\t\tvalue: {\n\t\t\t\ttype: \"expression\",\n\t\t\t\targ: { type: \"variable-reference\", name: \"count\" },\n\t\t\t\tannotation: { type: \"function-reference\", name: \"plural\", options: [] },\n\t\t\t},\n\t\t},\n\t\talias(\"pluralAlias\", \"countPlural\"),\n\t\t{\n\t\t\ttype: \"local-variable\",\n\t\t\tname: \"constant\",\n\t\t\tvalue: { type: \"expression\", arg: { type: \"literal\", value: \"0\" } },\n\t\t},\n\t\talias(\"undeclaredAlias\", \"missing\"),\n\t];\n\n\texpect(resolveInputAlias(\"countPlural\", declarations)).toBeUndefined();\n\texpect(resolveInputAlias(\"pluralAlias\", declarations)).toBeUndefined();\n\texpect(resolveInputAlias(\"constant\", declarations)).toBeUndefined();\n\texpect(resolveInputAlias(\"undeclaredAlias\", declarations)).toBeUndefined();\n\texpect(resolveInputAlias(\"missing\", declarations)).toBeUndefined();\n});\n\ntest(\"terminates on cyclic aliases\", () => {\n\tconst declarations: Declaration[] = [\n\t\talias(\"self\", \"self\"),\n\t\talias(\"a\", \"b\"),\n\t\talias(\"b\", \"c\"),\n\t\talias(\"c\", \"a\"),\n\t];\n\n\texpect(resolveInputAlias(\"self\", declarations)).toBeUndefined();\n\texpect(resolveInputAlias(\"a\", declarations)).toBeUndefined();\n\texpect(resolveInputAlias(\"c\", declarations)).toBeUndefined();\n});\n"]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Declaration, LocalVariable, Pattern } from "@inlang/sdk";
|
|
2
|
+
import type { CompilePatternMode } from "./compile-pattern.js";
|
|
3
|
+
/**
|
|
4
|
+
* Returns the names of the variables a compiled pattern reads.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors what `compilePattern()` emits: expression arguments, the options of
|
|
7
|
+
* registry function annotations (unknown annotations are dropped from the
|
|
8
|
+
* output), and in `parts` mode the options of markup.
|
|
9
|
+
*/
|
|
10
|
+
export declare function patternVariableReferences(pattern: Pattern, mode: CompilePatternMode): string[];
|
|
11
|
+
/**
|
|
12
|
+
* Returns the names of the variables a compiled local variable reads.
|
|
13
|
+
*
|
|
14
|
+
* Mirrors what `compileLocalVariable()` emits: the argument and the options
|
|
15
|
+
* of a registry function annotation (unknown annotations are dropped).
|
|
16
|
+
*/
|
|
17
|
+
export declare function localVariableReferences(declaration: LocalVariable): string[];
|
|
18
|
+
/**
|
|
19
|
+
* Resolves the local variables a message function has to declare.
|
|
20
|
+
*
|
|
21
|
+
* Starting from the variables the function body reads (patterns, match
|
|
22
|
+
* conditions), it follows local variables to the variables they read in turn
|
|
23
|
+
* and returns only the locals that are read, ordered so that every local is
|
|
24
|
+
* declared after the locals it reads. Locals the message does not read are
|
|
25
|
+
* left out, so the output passes `noUnusedLocals`.
|
|
26
|
+
*
|
|
27
|
+
* Throws if a read variable is not declared, or if locals reference each
|
|
28
|
+
* other in a cycle, because the emitted code would throw a `ReferenceError`
|
|
29
|
+
* at runtime.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // .input {$count}
|
|
33
|
+
* // .local countPlural = {$count :plural}
|
|
34
|
+
* // .local countPluralExact = {$count}
|
|
35
|
+
* resolveMessageLocals({ reads: ["countPlural"], ... })
|
|
36
|
+
* // { locals: [countPlural], readsInput: true }
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveMessageLocals(args: {
|
|
39
|
+
reads: Iterable<string>;
|
|
40
|
+
declarations: readonly Declaration[];
|
|
41
|
+
bundleId: string;
|
|
42
|
+
locale: string;
|
|
43
|
+
}): {
|
|
44
|
+
locals: LocalVariable[];
|
|
45
|
+
readsInput: boolean;
|
|
46
|
+
};
|