@wuchale/vite-plugin 0.10.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.
@@ -0,0 +1,39 @@
1
+ type SendFunc = (event: string, data: any[]) => void;
2
+ type HMRClient = {
3
+ send: SendFunc;
4
+ };
5
+ type ViteDevServer = {
6
+ ws: {
7
+ send: SendFunc;
8
+ on: (event: string, cb: (msg: {
9
+ loadID: string | null;
10
+ }, client: HMRClient) => void) => void;
11
+ };
12
+ };
13
+ declare class Plugin {
14
+ #private;
15
+ name: string;
16
+ constructor(configPath: string);
17
+ configResolved: (config: {
18
+ env: {
19
+ DEV?: boolean;
20
+ };
21
+ root: string;
22
+ }) => Promise<void>;
23
+ configureServer: (server: ViteDevServer) => void;
24
+ handleHotUpdate: (ctx: {
25
+ file: string;
26
+ }) => Promise<void>;
27
+ resolveId: (source: string, importer?: string) => string;
28
+ load: (id: string) => string;
29
+ transform: {
30
+ order: "pre";
31
+ handler: (code: string, id: string) => Promise<{
32
+ code?: string;
33
+ map?: any;
34
+ }>;
35
+ };
36
+ }
37
+ export declare const wuchale: (configPath?: string) => Plugin;
38
+ export {};
39
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,KAAK,QAAQ,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;AACpD,KAAK,SAAS,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,CAAA;AAEnC,KAAK,aAAa,GAAG;IACjB,EAAE,EAAE;QACA,IAAI,EAAE,QAAQ,CAAC;QACf,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE;YAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;SAAC,EAAE,MAAM,EAAE,SAAS,KAAK,IAAI,KAAK,IAAI,CAAC;KAC9F,CAAA;CACJ,CAAA;AAED,cAAM,MAAM;;IAER,IAAI,SAAa;gBAgBL,UAAU,EAAE,MAAM;IA8B9B,cAAc,GAAU,QAAQ;QAAE,GAAG,EAAE;YAAE,GAAG,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,mBASvE;IAED,eAAe,GAAI,QAAQ,aAAa,UAiBvC;IAeD,eAAe,GAAU,KAAK;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,mBAQ3C;IAED,SAAS,GAAI,QAAQ,MAAM,EAAE,WAAW,MAAM,YAK7C;IAED,IAAI,GAAI,IAAI,MAAM,YAiCjB;IAmBD,SAAS;eAAa,KAAK;wBAjBM,MAAM,MAAM,MAAM;;;;MAiBiB;CACvE;AAED,eAAO,MAAM,OAAO,GAAI,aAAa,MAAM,WAA2B,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,149 @@
1
+ // $$ cd ../.. && npm run test
2
+ import { relative, resolve } from "node:path";
3
+ import { getConfig as getConfig } from "wuchale/config";
4
+ import { AdapterHandler } from "wuchale/handler";
5
+ import { Logger } from "wuchale/log";
6
+ const pluginName = 'wuchale';
7
+ const virtualPrefix = `virtual:${pluginName}/`;
8
+ const virtualResolvedPrefix = '\0';
9
+ class Plugin {
10
+ name = pluginName;
11
+ #config;
12
+ #locales = [];
13
+ #projectRoot = '';
14
+ #server;
15
+ #adapters = {};
16
+ #adaptersByLoaderPath = {};
17
+ #adaptersByCatalogPath = {};
18
+ #log;
19
+ #configPath;
20
+ constructor(configPath) {
21
+ this.#configPath = configPath;
22
+ }
23
+ #init = async (mode) => {
24
+ this.#config = await getConfig(this.#configPath);
25
+ this.#locales = [this.#config.sourceLocale, ...this.#config.otherLocales];
26
+ this.#log = new Logger(this.#config.messages);
27
+ if (Object.keys(this.#config.adapters).length === 0) {
28
+ throw Error('At least one adapter is needed.');
29
+ }
30
+ for (const [key, adapter] of Object.entries(this.#config.adapters)) {
31
+ const handler = new AdapterHandler(adapter, key, this.#config, mode, virtualPrefix, this.#projectRoot, this.#log);
32
+ await handler.init();
33
+ this.#adapters[key] = handler;
34
+ this.#adaptersByLoaderPath[resolve(handler.loaderPath)] = handler;
35
+ for (const fname of Object.keys(handler.catalogPathsToLocales)) {
36
+ this.#adaptersByCatalogPath[fname] = handler;
37
+ }
38
+ }
39
+ };
40
+ configResolved = async (config) => {
41
+ let mode;
42
+ if (config.env.DEV) {
43
+ mode = 'dev';
44
+ }
45
+ else {
46
+ mode = 'prod';
47
+ }
48
+ this.#projectRoot = config.root;
49
+ await this.#init(mode);
50
+ };
51
+ configureServer = (server) => {
52
+ this.#server = server;
53
+ // initial load
54
+ for (const [key, adapter] of Object.entries(this.#adapters)) {
55
+ for (const loc of this.#locales) {
56
+ const event = adapter.virtModEvent(loc, null);
57
+ server.ws.on(event, (payload, client) => {
58
+ const eventSend = adapter.virtModEvent(loc, payload.loadID);
59
+ if (!this.#config.adapters[key].granularLoad) {
60
+ client.send(eventSend, adapter.compiled[loc].items);
61
+ return;
62
+ }
63
+ const compiled = adapter.granularStateByID[payload.loadID].compiled[loc];
64
+ client.send(eventSend, compiled.items);
65
+ });
66
+ }
67
+ }
68
+ };
69
+ #sendUpdateToClient = (adapter, locales) => {
70
+ for (const loc of locales) {
71
+ if (!this.#config.adapters[adapter.key].granularLoad) {
72
+ this.#server.ws.send(adapter.virtModEvent(loc, null), adapter.compiled[loc].items);
73
+ return;
74
+ }
75
+ for (const [loadID, state] of Object.entries(adapter.granularStateByID)) {
76
+ const eventName = adapter.virtModEvent(loc, loadID);
77
+ this.#server.ws.send(eventName, state.compiled[loc].items);
78
+ }
79
+ }
80
+ };
81
+ handleHotUpdate = async (ctx) => {
82
+ if (!(ctx.file in this.#adaptersByCatalogPath)) {
83
+ return;
84
+ }
85
+ const adapter = this.#adaptersByCatalogPath[ctx.file];
86
+ const loc = adapter.catalogPathsToLocales[ctx.file];
87
+ await adapter.loadCatalogNCompile(loc);
88
+ this.#sendUpdateToClient(adapter, [loc]);
89
+ };
90
+ resolveId = (source, importer) => {
91
+ if (!source.startsWith(virtualPrefix)) {
92
+ return null;
93
+ }
94
+ return `${virtualResolvedPrefix}${source}?${importer}`;
95
+ };
96
+ load = (id) => {
97
+ const prefix = virtualResolvedPrefix + virtualPrefix;
98
+ if (!id.startsWith(prefix)) {
99
+ return null;
100
+ }
101
+ const [path, importer] = id.slice(prefix.length).split('?');
102
+ const [part, ...rest] = path.split('/');
103
+ if (part === 'catalog') {
104
+ const [adapterKey, loadID, locale] = rest;
105
+ const adapter = this.#adapters[adapterKey];
106
+ if (adapter == null) {
107
+ this.#log.error(`Adapter not found for key: ${adapterKey}`);
108
+ return null;
109
+ }
110
+ return adapter.loadDataModule(locale, loadID);
111
+ }
112
+ if (part === 'locales') {
113
+ return `export const locales = ['${this.#locales.join("', '")}']`;
114
+ }
115
+ if (part !== 'proxy') {
116
+ this.#log.error(`Unknown virtual request: ${id}`);
117
+ return null;
118
+ }
119
+ // loader proxy
120
+ const adapter = this.#adaptersByLoaderPath[importer];
121
+ if (adapter == null) {
122
+ this.#log.error(`Adapter not found for filename: ${importer}`);
123
+ return;
124
+ }
125
+ if (rest[0] === 'sync') {
126
+ return adapter.getProxySync();
127
+ }
128
+ return adapter.getProxy();
129
+ };
130
+ #transformHandler = async (code, id) => {
131
+ if (!this.#config.hmr) {
132
+ return {};
133
+ }
134
+ const filename = relative(this.#projectRoot, id);
135
+ for (const adapter of Object.values(this.#adapters)) {
136
+ if (adapter.fileMatches(filename)) {
137
+ const { catalogChanged, ...output } = await adapter.transform(code, filename);
138
+ if (catalogChanged) {
139
+ this.#sendUpdateToClient(adapter, this.#locales);
140
+ }
141
+ return output;
142
+ }
143
+ }
144
+ return {};
145
+ };
146
+ transform = { order: 'pre', handler: this.#transformHandler };
147
+ }
148
+ export const wuchale = (configPath) => new Plugin(configPath);
149
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@wuchale/vite-plugin",
3
+ "version": "0.10.0",
4
+ "description": "Protobuf-like i18n from normal code",
5
+ "scripts": {
6
+ "dev": "tsc --watch",
7
+ "build": "tsc",
8
+ "test": "node tests"
9
+ },
10
+ "keywords": [
11
+ "i18n",
12
+ "internationalization",
13
+ "translation",
14
+ "gettext",
15
+ "vite",
16
+ "po",
17
+ "vite-plugin",
18
+ "compile-time",
19
+ "ast",
20
+ "gemini",
21
+ "translation-tooling",
22
+ "multilingual",
23
+ "localization",
24
+ "l10n",
25
+ "lingui",
26
+ "automatic-i18n",
27
+ "lightweight"
28
+ ],
29
+ "files": [
30
+ "dist",
31
+ "src"
32
+ ],
33
+ "main": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/wuchalejs/wuchale.git"
38
+ },
39
+ "homepage": "https://wuchale.dev",
40
+ "bugs": "https://github.com/wuchalejs/wuchale/issues",
41
+ "author": "K1DV5",
42
+ "license": "MIT",
43
+ "dependencies": {
44
+ "wuchale": "^0.10.0"
45
+ },
46
+ "devDependencies": {
47
+ "typescript": "^5.8.3"
48
+ },
49
+ "type": "module"
50
+ }
package/src/index.ts ADDED
@@ -0,0 +1,185 @@
1
+ // $$ cd ../.. && npm run test
2
+ import { relative, resolve } from "node:path"
3
+ import { getConfig as getConfig, type Config } from "wuchale/config"
4
+ import { AdapterHandler } from "wuchale/handler"
5
+ import type {Mode} from 'wuchale/handler'
6
+ import { Logger } from "wuchale/log"
7
+
8
+ const pluginName = 'wuchale'
9
+ const virtualPrefix = `virtual:${pluginName}/`
10
+ const virtualResolvedPrefix = '\0'
11
+
12
+ type SendFunc = (event: string, data: any[]) => void
13
+ type HMRClient = { send: SendFunc }
14
+
15
+ type ViteDevServer = {
16
+ ws: {
17
+ send: SendFunc,
18
+ on: (event: string, cb: (msg: {loadID: string | null}, client: HMRClient) => void) => void,
19
+ }
20
+ }
21
+
22
+ class Plugin {
23
+
24
+ name = pluginName
25
+
26
+ #config: Config
27
+ #locales: string[] = []
28
+ #projectRoot: string = ''
29
+
30
+ #server: ViteDevServer
31
+
32
+ #adapters: {[key: string]: AdapterHandler} = {}
33
+ #adaptersByLoaderPath: {[loader: string]: AdapterHandler} = {}
34
+ #adaptersByCatalogPath: {[path: string]: AdapterHandler} = {}
35
+
36
+ #log: Logger
37
+
38
+ #configPath: string
39
+
40
+ constructor(configPath: string) {
41
+ this.#configPath = configPath
42
+ }
43
+
44
+ #init = async (mode: Mode) => {
45
+ this.#config = await getConfig(this.#configPath)
46
+ this.#locales = [this.#config.sourceLocale, ...this.#config.otherLocales]
47
+ this.#log = new Logger(this.#config.messages)
48
+ if (Object.keys(this.#config.adapters).length === 0) {
49
+ throw Error('At least one adapter is needed.')
50
+ }
51
+ for (const [key, adapter] of Object.entries(this.#config.adapters)) {
52
+ const handler = new AdapterHandler(
53
+ adapter,
54
+ key,
55
+ this.#config,
56
+ mode,
57
+ virtualPrefix,
58
+ this.#projectRoot,
59
+ this.#log,
60
+ )
61
+ await handler.init()
62
+ this.#adapters[key] = handler
63
+ this.#adaptersByLoaderPath[resolve(handler.loaderPath)] = handler
64
+ for (const fname of Object.keys(handler.catalogPathsToLocales)) {
65
+ this.#adaptersByCatalogPath[fname] = handler
66
+ }
67
+ }
68
+ }
69
+
70
+ configResolved = async (config: { env: { DEV?: boolean }, root: string }) => {
71
+ let mode: Mode
72
+ if (config.env.DEV) {
73
+ mode = 'dev'
74
+ } else {
75
+ mode = 'prod'
76
+ }
77
+ this.#projectRoot = config.root
78
+ await this.#init(mode)
79
+ }
80
+
81
+ configureServer = (server: ViteDevServer) => {
82
+ this.#server = server
83
+ // initial load
84
+ for (const [key, adapter] of Object.entries(this.#adapters)) {
85
+ for (const loc of this.#locales) {
86
+ const event = adapter.virtModEvent(loc, null)
87
+ server.ws.on(event, (payload, client) => {
88
+ const eventSend = adapter.virtModEvent(loc, payload.loadID)
89
+ if (!this.#config.adapters[key].granularLoad) {
90
+ client.send(eventSend, adapter.compiled[loc].items)
91
+ return
92
+ }
93
+ const compiled = adapter.granularStateByID[payload.loadID].compiled[loc]
94
+ client.send(eventSend, compiled.items)
95
+ })
96
+ }
97
+ }
98
+ }
99
+
100
+ #sendUpdateToClient = (adapter: AdapterHandler, locales: string[]) => {
101
+ for (const loc of locales) {
102
+ if (!this.#config.adapters[adapter.key].granularLoad) {
103
+ this.#server.ws.send(adapter.virtModEvent(loc, null), adapter.compiled[loc].items)
104
+ return
105
+ }
106
+ for (const [loadID, state] of Object.entries(adapter.granularStateByID)) {
107
+ const eventName = adapter.virtModEvent(loc, loadID)
108
+ this.#server.ws.send(eventName, state.compiled[loc].items)
109
+ }
110
+ }
111
+ }
112
+
113
+ handleHotUpdate = async (ctx: {file: string}) => {
114
+ if (!(ctx.file in this.#adaptersByCatalogPath)) {
115
+ return
116
+ }
117
+ const adapter = this.#adaptersByCatalogPath[ctx.file]
118
+ const loc = adapter.catalogPathsToLocales[ctx.file]
119
+ await adapter.loadCatalogNCompile(loc)
120
+ this.#sendUpdateToClient(adapter, [loc])
121
+ }
122
+
123
+ resolveId = (source: string, importer?: string) => {
124
+ if (!source.startsWith(virtualPrefix)) {
125
+ return null
126
+ }
127
+ return `${virtualResolvedPrefix}${source}?${importer}`
128
+ }
129
+
130
+ load = (id: string) => {
131
+ const prefix = virtualResolvedPrefix + virtualPrefix
132
+ if (!id.startsWith(prefix)) {
133
+ return null
134
+ }
135
+ const [path, importer] = id.slice(prefix.length).split('?')
136
+ const [part, ...rest] = path.split('/')
137
+ if (part === 'catalog') {
138
+ const [adapterKey, loadID, locale] = rest
139
+ const adapter = this.#adapters[adapterKey]
140
+ if (adapter == null) {
141
+ this.#log.error(`Adapter not found for key: ${adapterKey}`)
142
+ return null
143
+ }
144
+ return adapter.loadDataModule(locale, loadID)
145
+ }
146
+ if (part === 'locales') {
147
+ return `export const locales = ['${this.#locales.join("', '")}']`
148
+ }
149
+ if (part !== 'proxy') {
150
+ this.#log.error(`Unknown virtual request: ${id}`)
151
+ return null
152
+ }
153
+ // loader proxy
154
+ const adapter = this.#adaptersByLoaderPath[importer]
155
+ if (adapter == null) {
156
+ this.#log.error(`Adapter not found for filename: ${importer}`)
157
+ return
158
+ }
159
+ if (rest[0] === 'sync') {
160
+ return adapter.getProxySync()
161
+ }
162
+ return adapter.getProxy()
163
+ }
164
+
165
+ #transformHandler = async (code: string, id: string) => {
166
+ if (!this.#config.hmr) {
167
+ return {}
168
+ }
169
+ const filename = relative(this.#projectRoot, id)
170
+ for (const adapter of Object.values(this.#adapters)) {
171
+ if (adapter.fileMatches(filename)) {
172
+ const {catalogChanged, ...output} = await adapter.transform(code, filename)
173
+ if (catalogChanged) {
174
+ this.#sendUpdateToClient(adapter, this.#locales)
175
+ }
176
+ return output
177
+ }
178
+ }
179
+ return {}
180
+ }
181
+
182
+ transform = { order: <'pre'>'pre', handler: this.#transformHandler }
183
+ }
184
+
185
+ export const wuchale = (configPath?: string) => new Plugin(configPath)