wuchale 0.8.2 → 0.9.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 +8 -317
- package/dist/src/adapter-vanilla.d.ts +17 -10
- package/dist/src/adapter-vanilla.d.ts.map +1 -0
- package/dist/src/adapter-vanilla.js +54 -39
- package/dist/src/adapter-vanilla.js.map +1 -1
- package/dist/src/adapters.d.ts +100 -0
- package/dist/src/adapters.d.ts.map +1 -0
- package/dist/src/{adapter.js → adapters.js} +18 -11
- package/dist/src/adapters.js.map +1 -0
- package/dist/src/cli.d.ts +1 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +78 -14
- package/dist/src/cli.js.map +1 -1
- package/dist/src/compile.d.ts +1 -0
- package/dist/src/compile.d.ts.map +1 -0
- package/dist/src/config.d.ts +4 -2
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +3 -2
- package/dist/src/config.js.map +1 -1
- package/dist/src/gemini.d.ts +1 -0
- package/dist/src/gemini.d.ts.map +1 -0
- package/dist/src/handler.d.ts +41 -19
- package/dist/src/handler.d.ts.map +1 -0
- package/dist/src/handler.js +304 -110
- package/dist/src/handler.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/plugin.d.ts +15 -16
- package/dist/src/plugin.d.ts.map +1 -0
- package/dist/src/plugin.js +72 -38
- package/dist/src/plugin.js.map +1 -1
- package/dist/src/run-client.d.ts +23 -0
- package/dist/src/run-client.d.ts.map +1 -0
- package/dist/src/run-client.js +55 -0
- package/dist/src/run-client.js.map +1 -0
- package/dist/src/run-server.d.ts +5 -0
- package/dist/src/run-server.d.ts.map +1 -0
- package/dist/src/run-server.js +26 -0
- package/dist/src/run-server.js.map +1 -0
- package/dist/src/runtime.d.ts +3 -8
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/runtime.js +2 -29
- package/dist/src/runtime.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +19 -18
- package/src/adapter-vanilla.ts +74 -46
- package/src/{adapter.ts → adapters.ts} +77 -40
- package/src/cli.ts +83 -17
- package/src/config.ts +5 -3
- package/src/handler.ts +323 -114
- package/src/loader.default.js +9 -0
- package/src/plugin.ts +76 -51
- package/src/run-client.ts +62 -0
- package/src/run-server.ts +30 -0
- package/src/runtime.ts +4 -40
- package/src/virtual.d.ts +18 -0
- package/dist/src/adapter.d.ts +0 -70
- package/dist/src/adapter.js.map +0 -1
- package/dist/src/runtime-server.d.ts +0 -4
- package/dist/src/runtime-server.js +0 -9
- package/dist/src/runtime-server.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,328 +1,19 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 📜`wuchale`🪶
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/wuchale) 
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
Inspired by Lingui, built from scratch with performance, clarity, and
|
|
7
|
-
simplicity in mind.
|
|
8
|
-
|
|
9
|
-
> 🎯 **Smart translations, tiny runtime, full HMR.** Extract strings at build
|
|
10
|
-
> time, generate optimized translation catalogs, support live translations
|
|
11
|
-
> (even with Gemini AI), and ship minimal code to production.
|
|
12
|
-
|
|
13
|
-
## Why `wuchale`?
|
|
14
|
-
|
|
15
|
-
Traditional i18n solutions require you to wrap every translatable string with
|
|
16
|
-
function calls or components. `wuchale` doesn't.
|
|
17
|
-
|
|
18
|
-
```typescript
|
|
19
|
-
// Traditional i18n
|
|
20
|
-
const t = i18n('Hello')
|
|
21
|
-
|
|
22
|
-
// With wuchale
|
|
23
|
-
const t = 'Hello'
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Write your code naturally. No imports, no wrappers, no annotations.
|
|
27
|
-
`wuchale` handles everything at compile time.
|
|
28
|
-
|
|
29
|
-
Try the live example in your browser, no setup required:
|
|
30
|
-
|
|
31
|
-
- Vanilla TS: [](https://stackblitz.com/github/K1DV5/wuchale/tree/main/examples/vanilla)
|
|
32
|
-
|
|
33
|
-
## ✨ Key Features
|
|
5
|
+
**`wuchale`** is a non-invasive, normal code based compile-time internationalization (i18n) toolkit.
|
|
34
6
|
|
|
7
|
+
- **🔤 No extra syntax!** - your normal code is enough
|
|
8
|
+
- **📦 Tiny catalogs to bundle** - Text catalogs are just arrays, no keys necessary
|
|
35
9
|
- **🔧 Zero-effort integration** - Add i18n to existing projects without rewriting code
|
|
36
10
|
- **🚀 Compile-time optimization** - All transformations happen during build, minimal runtime overhead
|
|
37
|
-
- **🔄 Full, granular HMR support** - Live updates during development, including auto-translation
|
|
38
|
-
- **📦 Tiny footprint** - Only 2 additional dependencies (`wuchale` + `pofile`), no bloated `node_modules`
|
|
11
|
+
- **🔄 Full, granular HMR support** - Live updates during development, including AI auto-translation
|
|
12
|
+
- **📦 Tiny footprint** - Only 2 or 3 additional dependencies (`wuchale` + `pofile`), no bloated `node_modules`
|
|
39
13
|
- **🎯 Smart extraction** - Uses AST analysis: handles nested markup, conditionals, loops, and complex interpolations
|
|
40
14
|
- **🌍 Standard .po files** - Compatible with existing translation tools and workflows
|
|
41
15
|
- **🤖 Optional AI translation** - Gemini integration for automatic translations during development
|
|
42
16
|
|
|
43
|
-
##
|
|
44
|
-
|
|
45
|
-
To use `wuchale` you need the main `vite` plugin (this package) and an adapter
|
|
46
|
-
for your project type. The following adapters are currently available:
|
|
47
|
-
|
|
48
|
-
- JavaScript/TypeScript (vanilla adapter): included in this package.
|
|
49
|
-
- Svelte: [here](https://www.npmjs.com/package/@wuchale/svelte)
|
|
50
|
-
|
|
51
|
-
## 🚀 Quick Start
|
|
52
|
-
|
|
53
|
-
We will use the vanilla adapter as an example.
|
|
54
|
-
|
|
55
|
-
### 1. Install
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
npm install wuchale
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### 2. Configure Vite
|
|
62
|
-
|
|
63
|
-
```javascript
|
|
64
|
-
// vite.config.js
|
|
65
|
-
import { wuchale } from 'wuchale'
|
|
66
|
-
|
|
67
|
-
export default {
|
|
68
|
-
plugins: [
|
|
69
|
-
wuchale(),
|
|
70
|
-
]
|
|
71
|
-
}
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### 3. Create Configuration
|
|
75
|
-
|
|
76
|
-
Create `wuchale.config.js` in your project root:
|
|
77
|
-
|
|
78
|
-
```javascript
|
|
79
|
-
// @ts-check
|
|
80
|
-
import { adapter as vanillaAdapter } from "wuchale/adapter-vanilla"
|
|
81
|
-
import { defineConfig } from "wuchale/config"
|
|
82
|
-
|
|
83
|
-
export default defineConfig({
|
|
84
|
-
locales: {
|
|
85
|
-
// English included by default
|
|
86
|
-
es: { name: 'Spanish' },
|
|
87
|
-
fr: { name: 'French' }
|
|
88
|
-
},
|
|
89
|
-
adapters: {
|
|
90
|
-
main: vanillaAdapter(),
|
|
91
|
-
}
|
|
92
|
-
})
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### 4. Create the locales directory
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
mkdir src/locales
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### 5. Add CLI Scripts
|
|
102
|
-
|
|
103
|
-
```jsonc
|
|
104
|
-
// package.json
|
|
105
|
-
{
|
|
106
|
-
"scripts": {
|
|
107
|
-
"extract": "wuchale",
|
|
108
|
-
"clean": "wuchale --clean"
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### 6. Use in Your App!
|
|
114
|
-
|
|
115
|
-
```javascript
|
|
116
|
-
// src/index.js
|
|
117
|
-
|
|
118
|
-
import { runWithCatalog } from 'wuchale/runtime-server'
|
|
119
|
-
|
|
120
|
-
const catalogs = {
|
|
121
|
-
en: await import(`./locales/en.js`),
|
|
122
|
-
es: await import(`./locales/es.js`),
|
|
123
|
-
fr: await import(`./locales/fr.js`),
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// adapted from an express.js example
|
|
127
|
-
// only to show how to use runWithCatalog
|
|
128
|
-
app.get('/', (req, res) => {
|
|
129
|
-
runWithCatalog(catalogs.es, () => res.send('Hello')) // will return Hola
|
|
130
|
-
})
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
### 7. Start Coding!
|
|
134
|
-
|
|
135
|
-
Write your code naturally. `wuchale` will extract and compile translations automatically:
|
|
136
|
-
|
|
137
|
-
```javascript
|
|
138
|
-
function eventHandler(event) {
|
|
139
|
-
event.target.value = 'Hello!'
|
|
140
|
-
}
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
## 📖 How It Works
|
|
144
|
-
|
|
145
|
-
[See main README](https://github.com/K1DV5/wuchale)
|
|
146
|
-
|
|
147
|
-
## AI Translation
|
|
148
|
-
|
|
149
|
-
Enable Gemini translations by setting `GEMINI_API_KEY`:
|
|
150
|
-
|
|
151
|
-
```bash
|
|
152
|
-
GEMINI_API_KEY=your-key npm run dev
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
## 📁 File Structure
|
|
156
|
-
|
|
157
|
-
```
|
|
158
|
-
src/
|
|
159
|
-
├── locales/
|
|
160
|
-
│ ├── en.po # Source catalog (commit this)
|
|
161
|
-
│ ├── en.js # Compiled data module (gitignore)
|
|
162
|
-
│ ├── es.po # Translation catalog (commit this)
|
|
163
|
-
│ └── es.js # Compiled data module (gitignore)
|
|
164
|
-
└── index.js # Your code
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
## 🧠 Behavior Explanation (vanilla adapter)
|
|
168
|
-
|
|
169
|
-
### What Gets Extracted?
|
|
170
|
-
|
|
171
|
-
This is decided by the heuristic function which you can customize. A sensible
|
|
172
|
-
default heuristic function is provided out of the box. Here's how it works:
|
|
173
|
-
|
|
174
|
-
- If the text contains no letters used in any natural language (e.g., just numbers or symbols), it is ignored.
|
|
175
|
-
- If it's in a top-level expression (not inside an assignment or a function definition) it is ignored.
|
|
176
|
-
- If the value is inside `console.*()` call, it is ignored.
|
|
177
|
-
- If the first character is a lowercase English letter (`[a-z]`) or is any non-letter, it is ignored.
|
|
178
|
-
- Otherwise, it is extracted.
|
|
179
|
-
|
|
180
|
-
Examples:
|
|
181
|
-
|
|
182
|
-
```javascript
|
|
183
|
-
|
|
184
|
-
const message = 'This is extracted'
|
|
185
|
-
const lowercase = 'not extracted'
|
|
186
|
-
|
|
187
|
-
// Force extraction with comment
|
|
188
|
-
const forced = /* @wc-include */ 'force extracted'
|
|
189
|
-
|
|
190
|
-
function foo() {
|
|
191
|
-
const extracted = 'Hello!'
|
|
192
|
-
}
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
If you need more control, you can supply your own heuristic function in the
|
|
196
|
-
configuration. Custom heuristics can return `undefined` or `null` to fall back
|
|
197
|
-
to the default. For convenience, the default heuristic is exported by the
|
|
198
|
-
package.
|
|
199
|
-
|
|
200
|
-
> 💡 You can override extraction with comment directives:
|
|
201
|
-
> - `@wc-ignore` — skips extraction
|
|
202
|
-
> - `@wc-include` — forces extraction
|
|
203
|
-
> These always take precedence.
|
|
204
|
-
|
|
205
|
-
### Pluralization
|
|
206
|
-
|
|
207
|
-
Define your function
|
|
208
|
-
```javascript
|
|
209
|
-
// in e.g. src/utils.js
|
|
210
|
-
export function plural(num, candidates, rule = n => n === 1 ? 0 : 1) {
|
|
211
|
-
const index = rule(num)
|
|
212
|
-
return candidates[index].replace('#', num)
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
Use it
|
|
217
|
-
|
|
218
|
-
```javascript
|
|
219
|
-
import {plural} from '/src/utils.js'
|
|
220
|
-
|
|
221
|
-
function eventHandler(e) {
|
|
222
|
-
let itemCount = 5
|
|
223
|
-
e.target.value = plural(itemCount, ['One item', '# items'])
|
|
224
|
-
}
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
### Context
|
|
228
|
-
|
|
229
|
-
Disambiguate identical texts:
|
|
230
|
-
|
|
231
|
-
```typescript
|
|
232
|
-
// @wc-context: navigation
|
|
233
|
-
const msg = 'Home'
|
|
234
|
-
|
|
235
|
-
// @wc-context: building
|
|
236
|
-
const bldg = 'Home'
|
|
237
|
-
```
|
|
238
|
-
### Useful Usage Pattern
|
|
239
|
-
|
|
240
|
-
A common scenario is needing to prevent string extraction inside functions, but
|
|
241
|
-
you may not want to modify the global heuristic or litter your code with
|
|
242
|
-
comment directives. A cleaner approach is to extract constants to the top
|
|
243
|
-
level, which are ignored by default:
|
|
244
|
-
|
|
245
|
-
```js
|
|
246
|
-
const keys = {
|
|
247
|
-
Escape: 'Escape',
|
|
248
|
-
ArrowUp: 'ArrowUp',
|
|
249
|
-
// ...
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
function eventHandler(event) {
|
|
253
|
-
if (event.key === keys.Escape) {
|
|
254
|
-
// ...
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
## 🛠️ Configuration Reference
|
|
260
|
-
|
|
261
|
-
### Main plugin
|
|
262
|
-
|
|
263
|
-
```javascript
|
|
264
|
-
export default {
|
|
265
|
-
// Source language code
|
|
266
|
-
sourceLocale: 'en',
|
|
267
|
-
|
|
268
|
-
// Available locales with plural rules
|
|
269
|
-
locales: {
|
|
270
|
-
en: {
|
|
271
|
-
name: 'English',
|
|
272
|
-
// the number of plurals in the language
|
|
273
|
-
nPlurals: 2,
|
|
274
|
-
// The expression to use to decide which candidate to choose when using your plural() function
|
|
275
|
-
// The number should be used as 'n' because this will be the body of an arrow function with n as an argument.
|
|
276
|
-
pluralRule: 'n == 1 ? 0 : 1'
|
|
277
|
-
}
|
|
278
|
-
},
|
|
279
|
-
|
|
280
|
-
// Adapters are the project type specific bindings for wuchale. For the vanilla adapter configuration, look below.
|
|
281
|
-
// You can repeat the same adapter with different keys and catalog configurations
|
|
282
|
-
// to break the translations into smaller parts
|
|
283
|
-
adapters: {
|
|
284
|
-
// key: AdapterConf
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Enable HMR updates during development. You can disable this to avoid the small overhead
|
|
288
|
-
// of live translation updates and work solely with the source language.
|
|
289
|
-
// HMR is highly optimized -- it updates only the affected components,
|
|
290
|
-
// preserving application state and avoiding full reloads.
|
|
291
|
-
hmr: true,
|
|
292
|
-
|
|
293
|
-
// Gemini API key (or 'env' to use GEMINI_API_KEY)
|
|
294
|
-
// if it's 'env', and GEMINI_API_KEY is not set, it is disabled
|
|
295
|
-
// set it to null to disable it entirely
|
|
296
|
-
geminiAPIKey: 'env'
|
|
297
|
-
}
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
### Basic Adapter
|
|
301
|
-
|
|
302
|
-
```javascript
|
|
303
|
-
|
|
304
|
-
import { adapter as vanillaAdapter } from "wuchale/adapter-vanilla"
|
|
17
|
+
## 📚 Documentation
|
|
305
18
|
|
|
306
|
-
|
|
307
|
-
// Where to store translation files. {locale} will be replaced with the respective locale.
|
|
308
|
-
catalog: './src/locales/{locale}',
|
|
309
|
-
|
|
310
|
-
// Files to scan for translations and transform
|
|
311
|
-
files: ['src/**/*.{js,ts}'],
|
|
312
|
-
|
|
313
|
-
// Custom extraction logic
|
|
314
|
-
// signature should be: (text: string, details: object) => boolean | undefined
|
|
315
|
-
// details has the following properties:
|
|
316
|
-
// scope: "markup" | "attribute" | "script",
|
|
317
|
-
// topLevel?: "variable" | "function" | "expression",
|
|
318
|
-
// topLevelCall?: string,
|
|
319
|
-
// call?: string,
|
|
320
|
-
// element?: string,
|
|
321
|
-
// attribute?: string,
|
|
322
|
-
// file?: string,
|
|
323
|
-
heuristic: defaultHeuristic,
|
|
324
|
-
|
|
325
|
-
// Your plural function name
|
|
326
|
-
pluralFunc: 'plural',
|
|
327
|
-
})
|
|
328
|
-
```
|
|
19
|
+
See the full guide at: [wuchale.dev](https://wuchale.dev/).
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import MagicString from "magic-string";
|
|
2
2
|
import type Estree from 'estree';
|
|
3
|
-
import { NestText } from './
|
|
4
|
-
import type {
|
|
3
|
+
import { NestText } from './adapters.js';
|
|
4
|
+
import type { AdapterArgs, Adapter, CommentDirectives, HeuristicDetailsBase, HeuristicFunc, IndexTracker, ScriptDeclType, TransformOutput } from "./adapters.js";
|
|
5
5
|
export declare function parseScript(content: string): import("acorn").Program;
|
|
6
|
-
export declare const runtimeConst = "
|
|
6
|
+
export declare const runtimeConst = "_w_runtime_";
|
|
7
7
|
export declare class Transformer {
|
|
8
8
|
index: IndexTracker;
|
|
9
9
|
heuristic: HeuristicFunc;
|
|
@@ -11,16 +11,17 @@ export declare class Transformer {
|
|
|
11
11
|
filename: string;
|
|
12
12
|
mstr: MagicString;
|
|
13
13
|
pluralFunc: string;
|
|
14
|
-
|
|
14
|
+
initInsideFuncLoadID: string | null;
|
|
15
15
|
rtFunc: string;
|
|
16
16
|
rtFuncPlural: string;
|
|
17
17
|
rtPluralsRule: string;
|
|
18
18
|
commentDirectives: CommentDirectives;
|
|
19
19
|
insideProgram: boolean;
|
|
20
|
-
|
|
20
|
+
declaring: ScriptDeclType;
|
|
21
|
+
insideFuncDef: boolean;
|
|
21
22
|
currentCall: string;
|
|
22
23
|
currentTopLevelCall: string;
|
|
23
|
-
constructor(
|
|
24
|
+
constructor(content: string, filename: string, index: IndexTracker, heuristic: HeuristicFunc, pluralsFunc: string, initInsideFuncLoadID?: string);
|
|
24
25
|
checkHeuristic: (text: string, detailsBase: HeuristicDetailsBase) => [boolean, NestText];
|
|
25
26
|
visitLiteral: (node: Estree.Literal & {
|
|
26
27
|
start: number;
|
|
@@ -48,8 +49,10 @@ export declare class Transformer {
|
|
|
48
49
|
visitVariableDeclaration: (node: Estree.VariableDeclaration) => NestText[];
|
|
49
50
|
visitExportNamedDeclaration: (node: Estree.ExportNamedDeclaration) => NestText[];
|
|
50
51
|
visitExportDefaultDeclaration: (node: Estree.ExportNamedDeclaration) => NestText[];
|
|
52
|
+
visitFunctionBody: (node: Estree.BlockStatement | Estree.Expression) => NestText[];
|
|
51
53
|
visitFunctionDeclaration: (node: Estree.FunctionDeclaration) => NestText[];
|
|
52
54
|
visitArrowFunctionExpression: (node: Estree.ArrowFunctionExpression) => NestText[];
|
|
55
|
+
visitFunctionExpression: (node: Estree.FunctionExpression) => NestText[];
|
|
53
56
|
visitBlockStatement: (node: Estree.BlockStatement) => NestText[];
|
|
54
57
|
visitReturnStatement: (node: Estree.ReturnStatement) => NestText[];
|
|
55
58
|
visitIfStatement: (node: Estree.IfStatement) => NestText[];
|
|
@@ -58,8 +61,12 @@ export declare class Transformer {
|
|
|
58
61
|
processCommentDirectives: (data: string) => CommentDirectives;
|
|
59
62
|
visit: (node: Estree.BaseNode) => NestText[];
|
|
60
63
|
finalize: (txts: NestText[]) => TransformOutput;
|
|
61
|
-
transform: () => TransformOutput;
|
|
64
|
+
transform: (loaderPath: string, loadID: string) => TransformOutput;
|
|
62
65
|
}
|
|
63
|
-
export declare const proxyModuleHotUpdate: (
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
export declare const proxyModuleHotUpdate: (loadID: string | null, eventSend: string, eventReceive: string, targetVar?: string) => string;
|
|
67
|
+
type VanillaAdapArgs = AdapterArgs & {
|
|
68
|
+
initInsideFunc?: boolean;
|
|
69
|
+
};
|
|
70
|
+
export declare const adapter: (args?: VanillaAdapArgs) => Adapter;
|
|
71
|
+
export {};
|
|
72
|
+
//# sourceMappingURL=adapter-vanilla.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-vanilla.d.ts","sourceRoot":"","sources":["../../src/adapter-vanilla.ts"],"names":[],"mappings":"AAEA,OAAO,WAAW,MAAM,cAAc,CAAA;AACtC,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAA;AAIhC,OAAO,EAAmD,QAAQ,EAAE,MAAM,eAAe,CAAA;AAEzF,OAAO,KAAK,EACR,WAAW,EACX,OAAO,EACP,iBAAiB,EACjB,oBAAoB,EACpB,aAAa,EACb,YAAY,EAEZ,cAAc,EACd,eAAe,EAClB,MAAM,eAAe,CAAA;AAUtB,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,2BAE1C;AAED,eAAO,MAAM,YAAY,gBAAgB,CAAA;AAGzC,qBAAa,WAAW;IAEpB,KAAK,EAAE,YAAY,CAAA;IACnB,SAAS,EAAE,aAAa,CAAA;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,WAAW,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;IAGnC,MAAM,SAAsB;IAC5B,YAAY,SAAuB;IACnC,aAAa,SAAwB;IAGrC,iBAAiB,EAAE,iBAAiB,CAAK;IACzC,aAAa,EAAE,OAAO,CAAQ;IAC9B,SAAS,EAAE,cAAc,CAAO;IAChC,aAAa,EAAE,OAAO,CAAQ;IAC9B,WAAW,EAAE,MAAM,CAAA;IACnB,mBAAmB,EAAE,MAAM,CAAA;gBAEf,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,EAAE,oBAAoB,CAAC,EAAE,MAAM;IAShJ,cAAc,GAAI,MAAM,MAAM,EAAE,aAAa,oBAAoB,KAAG,CAAC,OAAO,EAAE,QAAQ,CAAC,CAuBtF;IAED,YAAY,GAAI,MAAM,MAAM,CAAC,OAAO,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,KAAG,QAAQ,EAAE,CAWjF;IAED,oBAAoB,GAAI,MAAM,MAAM,CAAC,eAAe,KAAG,QAAQ,EAAE,CAMhE;IAED,qBAAqB,GAAI,MAAM,MAAM,CAAC,gBAAgB,KAAG,QAAQ,EAAE,CAMlE;IAED,aAAa,GAAI,MAAM,MAAM,CAAC,QAAQ,KAAG,QAAQ,EAAE,CAGlD;IAED,kBAAkB,GAAI,MAAM,MAAM,CAAC,aAAa,KAAG,QAAQ,EAAE,CAA6B;IAE1F,qBAAqB,GAAI,MAAM,MAAM,CAAC,gBAAgB,KAAG,QAAQ,EAAE,CAGlE;IAED,kBAAkB,GAAI,MAAM,MAAM,CAAC,aAAa,KAAG,QAAQ,EAAE,CAAyC;IAEtG,0BAA0B,GAAI,MAAM,MAAM,CAAC,cAAc,KAAG,QAAQ,EAAE,CASrE;IAED,mBAAmB,GAAI,MAAM,MAAM,CAAC,cAAc,KAAG,QAAQ,EAAE,CAuB9D;IAED,qBAAqB,GAAI,MAAM,MAAM,CAAC,gBAAgB,KAAG,QAAQ,EAAE,CAGlE;IAED,oBAAoB,GAAI,MAAM,MAAM,CAAC,eAAe,KAAG,QAAQ,EAAE,CAA6B;IAE9F,sBAAsB,GAAI,MAAM,MAAM,CAAC,iBAAiB,KAAG,QAAQ,EAAE,CAGpE;IAED,oBAAoB,GAAI,MAAM,MAAM,CAAC,eAAe,KAAG,QAAQ,EAAE,CAA6B;IAE9F,yBAAyB,SAdM,MAAM,CAAC,gBAAgB,KAAG,QAAQ,EAAE,CAcb;IAEtD,wBAAwB,GAAI,MAAM,MAAM,CAAC,mBAAmB,KAAG,QAAQ,EAAE,CAA+B;IAExG,mBAAmB,GAAI,MAAM,MAAM,CAAC,cAAc,KAAG,QAAQ,EAAE,CAI9D;IAED,mBAAmB,GAAI,MAAM,MAAM,CAAC,cAAc,KAAG,QAAQ,EAAE,CAI9D;IAED,iBAAiB,GAAI,MAAM,MAAM,CAAC,YAAY,KAAG,QAAQ,EAAE,CAK1D;IAED,kBAAkB,GAAI,MAAM,MAAM,CAAC,gBAAgB,KAAG,MAAM,CAkB3D;IAED,aAAa,GAAI,QAAQ,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,KAAK,KAAG,MAAM,CAQjE;IAED,wBAAwB,GAAI,MAAM,MAAM,CAAC,mBAAmB,KAAG,QAAQ,EAAE,CA6BxE;IAED,2BAA2B,GAAI,MAAM,MAAM,CAAC,sBAAsB,KAAG,QAAQ,EAAE,CAAwD;IAEvI,6BAA6B,SAFQ,MAAM,CAAC,sBAAsB,KAAG,QAAQ,EAAE,CAEf;IAEhE,iBAAiB,GAAI,MAAM,MAAM,CAAC,cAAc,GAAG,MAAM,CAAC,UAAU,KAAG,QAAQ,EAAE,CAUhF;IAED,wBAAwB,GAAI,MAAM,MAAM,CAAC,mBAAmB,KAAG,QAAQ,EAAE,CAMxE;IAED,4BAA4B,GAAI,MAAM,MAAM,CAAC,uBAAuB,KAAG,QAAQ,EAAE,CAAqC;IAEtH,uBAAuB,GAAI,MAAM,MAAM,CAAC,kBAAkB,KAAG,QAAQ,EAAE,CAAqC;IAE5G,mBAAmB,GAAI,MAAM,MAAM,CAAC,cAAc,KAAG,QAAQ,EAAE,CAM9D;IAED,oBAAoB,GAAI,MAAM,MAAM,CAAC,eAAe,KAAG,QAAQ,EAAE,CAKhE;IAED,gBAAgB,GAAI,MAAM,MAAM,CAAC,WAAW,KAAG,QAAQ,EAAE,CAOxD;IAED,oBAAoB,GAAI,MAAM,MAAM,CAAC,eAAe,KAAG,QAAQ,EAAE,CA4ChE;IAED,YAAY,GAAI,MAAM,MAAM,CAAC,OAAO,KAAG,QAAQ,EAAE,CAQhD;IAED,wBAAwB,GAAI,MAAM,MAAM,KAAG,iBAAiB,CAa3D;IAED,KAAK,GAAI,MAAM,MAAM,CAAC,QAAQ,KAAG,QAAQ,EAAE,CAiB1C;IAED,QAAQ,GAAI,MAAM,QAAQ,EAAE,KAAG,eAAe,CAU7C;IAED,SAAS,GAAI,YAAY,MAAM,EAAE,QAAQ,MAAM,KAAG,eAAe,CAYhE;CACJ;AAED,eAAO,MAAM,oBAAoB,GAAI,QAAQ,MAAM,GAAG,IAAI,EAAE,WAAW,MAAM,EAAE,cAAc,MAAM,EAAE,kBAAkB,WAWtH,CAAA;AAQD,KAAK,eAAe,GAAG,WAAW,GAAG;IAAC,cAAc,CAAC,EAAE,OAAO,CAAA;CAAC,CAAA;AAa/D,eAAO,MAAM,OAAO,GAAI,OAAM,eAA6B,KAAG,OAyB7D,CAAA"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import MagicString from "magic-string";
|
|
3
3
|
import { Parser } from 'acorn';
|
|
4
4
|
import { tsPlugin } from '@sveltejs/acorn-typescript';
|
|
5
|
-
import { defaultHeuristicFuncOnly, NestText } from './
|
|
5
|
+
import { defaultGenerateLoadID, defaultHeuristicFuncOnly, NestText } from './adapters.js';
|
|
6
6
|
import { deepMergeObjects } from "./config.js";
|
|
7
7
|
const scriptParseOptions = {
|
|
8
8
|
sourceType: 'module',
|
|
@@ -13,7 +13,8 @@ const ScriptParser = Parser.extend(tsPlugin());
|
|
|
13
13
|
export function parseScript(content) {
|
|
14
14
|
return ScriptParser.parse(content, scriptParseOptions);
|
|
15
15
|
}
|
|
16
|
-
export const runtimeConst = '
|
|
16
|
+
export const runtimeConst = '_w_runtime_';
|
|
17
|
+
const rtFuncInit = '_w_load_';
|
|
17
18
|
export class Transformer {
|
|
18
19
|
index;
|
|
19
20
|
heuristic;
|
|
@@ -21,24 +22,25 @@ export class Transformer {
|
|
|
21
22
|
filename;
|
|
22
23
|
mstr;
|
|
23
24
|
pluralFunc;
|
|
25
|
+
initInsideFuncLoadID;
|
|
24
26
|
// for runtime
|
|
25
|
-
key;
|
|
26
27
|
rtFunc = `${runtimeConst}.t`;
|
|
27
28
|
rtFuncPlural = `${runtimeConst}.tp`;
|
|
28
29
|
rtPluralsRule = `${runtimeConst}.plr`;
|
|
29
30
|
// state
|
|
30
31
|
commentDirectives = {};
|
|
31
32
|
insideProgram = false;
|
|
32
|
-
|
|
33
|
+
declaring = null;
|
|
34
|
+
insideFuncDef = false;
|
|
33
35
|
currentCall;
|
|
34
36
|
currentTopLevelCall;
|
|
35
|
-
constructor(
|
|
36
|
-
this.key = key;
|
|
37
|
+
constructor(content, filename, index, heuristic, pluralsFunc, initInsideFuncLoadID) {
|
|
37
38
|
this.index = index;
|
|
38
39
|
this.heuristic = heuristic;
|
|
39
40
|
this.pluralFunc = pluralsFunc;
|
|
40
41
|
this.content = content;
|
|
41
42
|
this.filename = filename;
|
|
43
|
+
this.initInsideFuncLoadID = initInsideFuncLoadID;
|
|
42
44
|
}
|
|
43
45
|
checkHeuristic = (text, detailsBase) => {
|
|
44
46
|
if (!text) {
|
|
@@ -50,7 +52,8 @@ export class Transformer {
|
|
|
50
52
|
const details = {
|
|
51
53
|
file: this.filename,
|
|
52
54
|
call: this.currentCall,
|
|
53
|
-
topLevel: this.
|
|
55
|
+
topLevel: this.declaring,
|
|
56
|
+
insideFuncDef: this.insideFuncDef,
|
|
54
57
|
topLevelCall: this.currentTopLevelCall,
|
|
55
58
|
...detailsBase,
|
|
56
59
|
};
|
|
@@ -195,7 +198,7 @@ export class Transformer {
|
|
|
195
198
|
};
|
|
196
199
|
visitVariableDeclaration = (node) => {
|
|
197
200
|
const txts = [];
|
|
198
|
-
let atTopLevelDefn = this.insideProgram && !this.
|
|
201
|
+
let atTopLevelDefn = this.insideProgram && !this.declaring;
|
|
199
202
|
for (const dec of node.declarations) {
|
|
200
203
|
if (!dec.init) {
|
|
201
204
|
continue;
|
|
@@ -203,10 +206,10 @@ export class Transformer {
|
|
|
203
206
|
// store the name of the function after =
|
|
204
207
|
if (atTopLevelDefn) {
|
|
205
208
|
if (dec.init.type === 'ArrowFunctionExpression') {
|
|
206
|
-
this.
|
|
209
|
+
this.declaring = 'function';
|
|
207
210
|
}
|
|
208
211
|
else {
|
|
209
|
-
this.
|
|
212
|
+
this.declaring = 'variable';
|
|
210
213
|
if (dec.init.type === 'CallExpression') {
|
|
211
214
|
this.currentTopLevelCall = this.getCalleeName(dec.init.callee);
|
|
212
215
|
}
|
|
@@ -220,22 +223,32 @@ export class Transformer {
|
|
|
220
223
|
}
|
|
221
224
|
if (atTopLevelDefn) {
|
|
222
225
|
this.currentTopLevelCall = null; // reset
|
|
223
|
-
this.
|
|
226
|
+
this.declaring = null;
|
|
224
227
|
}
|
|
225
228
|
return txts;
|
|
226
229
|
};
|
|
227
230
|
visitExportNamedDeclaration = (node) => node.declaration ? this.visit(node.declaration) : [];
|
|
228
231
|
visitExportDefaultDeclaration = this.visitExportNamedDeclaration;
|
|
232
|
+
visitFunctionBody = (node) => {
|
|
233
|
+
const insideFuncDef = this.insideFuncDef;
|
|
234
|
+
this.insideFuncDef = true;
|
|
235
|
+
const txts = this.visit(node);
|
|
236
|
+
if (this.initInsideFuncLoadID && node.type === 'BlockStatement') {
|
|
237
|
+
// @ts-expect-error
|
|
238
|
+
this.mstr.prependLeft(node.start + 1, `const ${runtimeConst} = ${rtFuncInit}('${this.initInsideFuncLoadID}')\n`);
|
|
239
|
+
}
|
|
240
|
+
this.insideFuncDef = insideFuncDef;
|
|
241
|
+
return txts;
|
|
242
|
+
};
|
|
229
243
|
visitFunctionDeclaration = (node) => {
|
|
230
|
-
const
|
|
231
|
-
this.
|
|
232
|
-
const txts = this.
|
|
233
|
-
|
|
234
|
-
this.topLevel = null;
|
|
235
|
-
}
|
|
244
|
+
const declaring = this.declaring;
|
|
245
|
+
this.declaring = 'function';
|
|
246
|
+
const txts = this.visitFunctionBody(node.body);
|
|
247
|
+
this.declaring = declaring;
|
|
236
248
|
return txts;
|
|
237
249
|
};
|
|
238
|
-
visitArrowFunctionExpression = (node) => this.
|
|
250
|
+
visitArrowFunctionExpression = (node) => this.visitFunctionBody(node.body);
|
|
251
|
+
visitFunctionExpression = (node) => this.visitFunctionBody(node.body);
|
|
239
252
|
visitBlockStatement = (node) => {
|
|
240
253
|
const txts = [];
|
|
241
254
|
for (const statement of node.body) {
|
|
@@ -355,59 +368,61 @@ export class Transformer {
|
|
|
355
368
|
map: this.mstr.generateMap(),
|
|
356
369
|
};
|
|
357
370
|
};
|
|
358
|
-
transform = () => {
|
|
371
|
+
transform = (loaderPath, loadID) => {
|
|
359
372
|
const ast = parseScript(this.content);
|
|
360
373
|
this.mstr = new MagicString(this.content);
|
|
361
374
|
const txts = this.visit(ast);
|
|
362
375
|
if (txts.length) {
|
|
363
|
-
const collectionFunc = '_wre_';
|
|
364
376
|
const importModule = `
|
|
365
|
-
import
|
|
366
|
-
const ${runtimeConst} = ${
|
|
377
|
+
import ${rtFuncInit} from "${loaderPath}"
|
|
378
|
+
const ${runtimeConst} = ${rtFuncInit}('${loadID}')
|
|
367
379
|
`;
|
|
368
380
|
this.mstr.appendRight(0, importModule);
|
|
369
381
|
}
|
|
370
382
|
return this.finalize(txts);
|
|
371
383
|
};
|
|
372
384
|
}
|
|
373
|
-
export const proxyModuleHotUpdate = (
|
|
385
|
+
export const proxyModuleHotUpdate = (loadID, eventSend, eventReceive, targetVar = 'data') => `
|
|
374
386
|
if (import.meta.hot) {
|
|
375
|
-
import.meta.hot.on('${
|
|
387
|
+
import.meta.hot.on('${eventSend}', newData => {
|
|
376
388
|
for (let i = 0; i < newData.length; i++) {
|
|
377
389
|
if (JSON.stringify(data[i]) !== JSON.stringify(newData[i])) {
|
|
378
390
|
${targetVar}[i] = newData[i]
|
|
379
391
|
}
|
|
380
392
|
}
|
|
381
393
|
})
|
|
382
|
-
import.meta.hot.send('${
|
|
394
|
+
import.meta.hot.send('${eventReceive}'${loadID == null ? '' : `, {loadID: '${loadID}'}`})
|
|
383
395
|
}
|
|
384
396
|
`;
|
|
385
|
-
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
${proxyModuleHotUpdate(
|
|
389
|
-
export {key, pluralsRule}
|
|
390
|
-
export default data
|
|
397
|
+
const dataModuleDev = ({ loadID: loadID, eventSend, eventReceive, compiled, plural }) => `
|
|
398
|
+
export const plural = ${plural}
|
|
399
|
+
export const data = ${compiled}
|
|
400
|
+
${proxyModuleHotUpdate(loadID, eventSend, eventReceive)}
|
|
391
401
|
`;
|
|
392
402
|
const defaultArgs = {
|
|
393
403
|
files: ['src/**/*.{js,ts}'],
|
|
394
404
|
catalog: './src/locales/{locale}',
|
|
395
405
|
pluralsFunc: 'plural',
|
|
396
406
|
heuristic: defaultHeuristicFuncOnly,
|
|
407
|
+
generateLoadID: defaultGenerateLoadID,
|
|
408
|
+
granularLoad: false,
|
|
409
|
+
writeFiles: {},
|
|
410
|
+
initInsideFunc: false,
|
|
397
411
|
};
|
|
398
412
|
export const adapter = (args = defaultArgs) => {
|
|
399
|
-
const { heuristic, pluralsFunc, files, catalog } = deepMergeObjects(args, defaultArgs);
|
|
413
|
+
const { heuristic, pluralsFunc, files, catalog, granularLoad, generateLoadID: generateID, writeFiles, initInsideFunc, } = deepMergeObjects(args, defaultArgs);
|
|
400
414
|
return {
|
|
401
|
-
transform: (content, filename, index,
|
|
402
|
-
return new Transformer(
|
|
415
|
+
transform: ({ content, filename, index, loaderPath, loadID }) => {
|
|
416
|
+
return new Transformer(content, filename, index, heuristic, pluralsFunc, initInsideFunc ? loadID : null).transform(loaderPath, loadID);
|
|
403
417
|
},
|
|
404
418
|
files,
|
|
405
419
|
catalog,
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
420
|
+
granularLoad,
|
|
421
|
+
generateLoadID: generateID,
|
|
422
|
+
loaderExts: ['.js', '.ts'],
|
|
423
|
+
dataModuleDev,
|
|
424
|
+
writeFiles,
|
|
425
|
+
defaultLoaderPath: () => new URL('../../src/loader.default.js', import.meta.url).pathname,
|
|
411
426
|
};
|
|
412
427
|
};
|
|
413
428
|
//# sourceMappingURL=adapter-vanilla.js.map
|