zintljs 0.1.0-alpha.7
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/LICENSE +21 -0
- package/README.md +253 -0
- package/dist/assemble-DJKgHgSM.mjs +348 -0
- package/dist/facets.d.mts +56 -0
- package/dist/facets.mjs +3 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +2 -0
- package/dist/macro.d.mts +148 -0
- package/dist/macro.mjs +130 -0
- package/dist/types-DTRiQkFi.d.mts +243 -0
- package/dist/vite.d.mts +43 -0
- package/dist/vite.mjs +1056 -0
- package/package.json +82 -0
package/dist/vite.mjs
ADDED
|
@@ -0,0 +1,1056 @@
|
|
|
1
|
+
import { o as detectFrameworksOrFallback, s as resolveFacets, t as assembleFacets } from "./assemble-DJKgHgSM.mjs";
|
|
2
|
+
import { createUnplugin } from "unplugin";
|
|
3
|
+
import { ZintlCompiler, generateMessageId, getRuntimeCode, sha1 } from "@zintljs/compiler";
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import { dirname, isAbsolute, join, relative } from "node:path";
|
|
6
|
+
//#region src/constants.ts
|
|
7
|
+
const VIRTUAL_PREFIX = "virtual:zintl-catalog";
|
|
8
|
+
const CHUNK_VIRTUAL_PREFIX = "virtual:zintl/catalog";
|
|
9
|
+
const CONTENT_VIRTUAL_PREFIX = "virtual:zintl/content";
|
|
10
|
+
const MANAGER_VIRTUAL_PREFIX = "virtual:zintl/manager";
|
|
11
|
+
const RESOLVED_VIRTUAL_PREFIX = "\0virtual:zintl-catalog";
|
|
12
|
+
const PLUGIN_NAME = "zintl";
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/context.ts
|
|
15
|
+
var Context = class {
|
|
16
|
+
options;
|
|
17
|
+
compiler;
|
|
18
|
+
server = null;
|
|
19
|
+
multiplexEnabled = null;
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.options = options;
|
|
22
|
+
}
|
|
23
|
+
getMultiplex(config) {
|
|
24
|
+
if (this.compiler && this.multiplexEnabled !== null) return this.multiplexEnabled;
|
|
25
|
+
const root = config?.root || this.compiler?.rootDir || process.cwd();
|
|
26
|
+
if (this.options.multiplex !== void 0) {
|
|
27
|
+
const val = this.options.multiplex;
|
|
28
|
+
if (this.compiler) this.multiplexEnabled = val;
|
|
29
|
+
return val;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const entryFiles = [];
|
|
33
|
+
const userInput = ((config?.build || {}).rollupOptions || {}).input;
|
|
34
|
+
if (userInput) {
|
|
35
|
+
if (typeof userInput === "string") entryFiles.push(userInput);
|
|
36
|
+
else if (Array.isArray(userInput)) entryFiles.push(...userInput);
|
|
37
|
+
else if (typeof userInput === "object" && userInput !== null) entryFiles.push(...Object.values(userInput));
|
|
38
|
+
}
|
|
39
|
+
if (entryFiles.length === 0) entryFiles.push("index.html");
|
|
40
|
+
const locales = this.options.locales;
|
|
41
|
+
const cleanEntryFiles = entryFiles.map((file) => {
|
|
42
|
+
let clean = file;
|
|
43
|
+
for (const loc of locales) if (clean.startsWith(`${loc}/`)) {
|
|
44
|
+
clean = clean.slice(loc.length + 1);
|
|
45
|
+
break;
|
|
46
|
+
} else if (clean.startsWith(`./${loc}/`)) {
|
|
47
|
+
clean = clean.slice(loc.length + 3);
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
return clean;
|
|
51
|
+
});
|
|
52
|
+
let content = "";
|
|
53
|
+
for (const entryFile of cleanEntryFiles) {
|
|
54
|
+
const absoluteEntryPath = isAbsolute(entryFile) ? entryFile : join(root, entryFile);
|
|
55
|
+
if (existsSync(absoluteEntryPath)) {
|
|
56
|
+
const fileContent = readFileSync(absoluteEntryPath, "utf-8");
|
|
57
|
+
content += fileContent;
|
|
58
|
+
if (entryFile.endsWith(".html")) {
|
|
59
|
+
const scriptMatches = fileContent.matchAll(/<script[^>]+src=["']([^"']+)["']/g);
|
|
60
|
+
for (const match of scriptMatches) {
|
|
61
|
+
const scriptSrc = match[1];
|
|
62
|
+
const cleanScriptSrc = scriptSrc.startsWith("/") ? scriptSrc.slice(1) : scriptSrc;
|
|
63
|
+
const absoluteScriptPath = isAbsolute(cleanScriptSrc) ? cleanScriptSrc : join(root, cleanScriptSrc);
|
|
64
|
+
if (existsSync(absoluteScriptPath)) {
|
|
65
|
+
const scriptContent = readFileSync(absoluteScriptPath, "utf-8");
|
|
66
|
+
content += scriptContent;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const mainPath = join(root, "src/main.ts");
|
|
73
|
+
const indexTsPath = join(root, "src/index.ts");
|
|
74
|
+
if (existsSync(mainPath)) content += readFileSync(mainPath, "utf-8");
|
|
75
|
+
if (existsSync(indexTsPath)) content += readFileSync(indexTsPath, "utf-8");
|
|
76
|
+
let result = false;
|
|
77
|
+
if (/zintl\(\s*['"]\*['"]\s*\)/.test(content) || /zintl\(\s*\)/.test(content)) result = true;
|
|
78
|
+
else result = false;
|
|
79
|
+
if (this.compiler) this.multiplexEnabled = result;
|
|
80
|
+
return result;
|
|
81
|
+
} catch {
|
|
82
|
+
if (this.compiler) this.multiplexEnabled = false;
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
getMultiplexLocale(id) {
|
|
87
|
+
const matchSuffix = id.match(/\.zintl-([a-zA-Z0-9_-]+)\.(vue|svelte)/);
|
|
88
|
+
if (matchSuffix) return matchSuffix[1];
|
|
89
|
+
if (!id.includes("zintl-multiplex=")) return void 0;
|
|
90
|
+
const match = id.match(/zintl-multiplex=([^&]+)/);
|
|
91
|
+
return match ? match[1] : void 0;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
//#endregion
|
|
95
|
+
//#region src/hooks/config.ts
|
|
96
|
+
function configHook(ctx) {
|
|
97
|
+
return function(userConfig) {
|
|
98
|
+
const multiplex = ctx.getMultiplex(userConfig);
|
|
99
|
+
const locales = ctx.options.locales;
|
|
100
|
+
const configUpdate = {};
|
|
101
|
+
if (ctx.options.debug) configUpdate.define = { "process.env.ZINTL_DEBUG": JSON.stringify(ctx.options.debug === true ? "true" : ctx.options.debug) };
|
|
102
|
+
if (multiplex) {
|
|
103
|
+
const userInput = ((userConfig.build || {}).rollupOptions || {}).input || "index.html";
|
|
104
|
+
const inputObj = {};
|
|
105
|
+
if (typeof userInput === "string") inputObj.index = userInput;
|
|
106
|
+
else if (Array.isArray(userInput)) userInput.forEach((inp, idx) => {
|
|
107
|
+
const name = inp.replace(/\.html$/, "").replace(/[^a-zA-Z0-9]/g, "_");
|
|
108
|
+
inputObj[name || `input_${idx}`] = inp;
|
|
109
|
+
});
|
|
110
|
+
else if (typeof userInput === "object" && userInput !== null) Object.assign(inputObj, userInput);
|
|
111
|
+
for (const [key, val] of Object.entries(inputObj)) for (const loc of locales) if (val.startsWith(`${loc}/`) || val.startsWith(`./${loc}/`)) delete inputObj[key];
|
|
112
|
+
if (Object.keys(inputObj).length === 0) inputObj.index = "index.html";
|
|
113
|
+
const root = userConfig.root || process.cwd();
|
|
114
|
+
const expandedInput = { ...inputObj };
|
|
115
|
+
for (const [key, val] of Object.entries(inputObj)) if (val.endsWith(".html")) {
|
|
116
|
+
const relativeVal = isAbsolute(val) ? relative(root, val) : val;
|
|
117
|
+
for (const loc of locales) {
|
|
118
|
+
const prefixKey = `${loc}/${key === "main" || key === "index" ? "index" : key}`;
|
|
119
|
+
expandedInput[prefixKey] = `${loc}/${relativeVal}`;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
configUpdate.build = { rollupOptions: { input: expandedInput } };
|
|
123
|
+
}
|
|
124
|
+
configUpdate.optimizeDeps = { exclude: [
|
|
125
|
+
...userConfig.optimizeDeps?.exclude || [],
|
|
126
|
+
"zintl",
|
|
127
|
+
"zintl/internal",
|
|
128
|
+
"virtual:zintl/runtime",
|
|
129
|
+
"virtual:zintl/runtime/internal"
|
|
130
|
+
] };
|
|
131
|
+
return configUpdate;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function configResolvedHook(ctx) {
|
|
135
|
+
return function(config) {
|
|
136
|
+
const logLevel = ctx.options.logLevel ?? config.logLevel ?? "info";
|
|
137
|
+
const verifyIntegrity = ctx.options.verifyIntegrity ?? config.command === "build";
|
|
138
|
+
const capabilities = resolveFacets(assembleFacets({
|
|
139
|
+
frameworks: detectFrameworksOrFallback({
|
|
140
|
+
pluginNames: Array.isArray(config.plugins) ? config.plugins.map((p) => p?.name).filter(Boolean) : [],
|
|
141
|
+
root: config.root
|
|
142
|
+
}),
|
|
143
|
+
ssr: Boolean(config.build?.ssr) || config.ssr !== void 0,
|
|
144
|
+
facets: ctx.options.facets,
|
|
145
|
+
assetsTarget: ctx.options.assetsTarget,
|
|
146
|
+
virtualAssets: ctx.options.virtualAssets
|
|
147
|
+
}));
|
|
148
|
+
ctx.compiler = new ZintlCompiler({
|
|
149
|
+
...ctx.options,
|
|
150
|
+
capabilities,
|
|
151
|
+
logLevel,
|
|
152
|
+
verifyIntegrity
|
|
153
|
+
}, config.root, config.command === "serve");
|
|
154
|
+
ctx.getMultiplex(config);
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/hooks/server.ts
|
|
159
|
+
function configureServerHook(ctx) {
|
|
160
|
+
return function(server) {
|
|
161
|
+
ctx.server = server;
|
|
162
|
+
if (server.config?.appType === "custom") return;
|
|
163
|
+
if (ctx.getMultiplex()) {
|
|
164
|
+
const locales = ctx.options.locales;
|
|
165
|
+
server.middlewares.use(async (req, res, next) => {
|
|
166
|
+
const url = req.url || "/";
|
|
167
|
+
const [pathname] = url.split("?");
|
|
168
|
+
const parts = pathname.split("/").filter(Boolean);
|
|
169
|
+
if (parts.length > 0 && locales.includes(parts[0])) {
|
|
170
|
+
const locale = parts[0];
|
|
171
|
+
const htmlFilename = parts.slice(1).join("/") || "index.html";
|
|
172
|
+
const originalPath = join(server.config.root, htmlFilename);
|
|
173
|
+
if (existsSync(originalPath)) {
|
|
174
|
+
let html = readFileSync(originalPath, "utf-8");
|
|
175
|
+
let dir = locale === "ar" ? "rtl" : "ltr";
|
|
176
|
+
try {
|
|
177
|
+
const catalogPath = ctx.compiler.html.getCatalogPath(htmlFilename, locale);
|
|
178
|
+
if (existsSync(catalogPath)) {
|
|
179
|
+
const cat = JSON.parse(readFileSync(catalogPath, "utf-8"));
|
|
180
|
+
const catalogDir = ctx.compiler.isMultilingualFormat() ? cat.dir?.[locale] : cat.dir;
|
|
181
|
+
if (catalogDir !== void 0) dir = catalogDir;
|
|
182
|
+
}
|
|
183
|
+
} catch {}
|
|
184
|
+
html = html.replace(/<html([^>]*)>/i, (m, attrs) => {
|
|
185
|
+
let newAttrs = attrs;
|
|
186
|
+
if (!/lang=/i.test(attrs)) newAttrs += ` lang="${locale}"`;
|
|
187
|
+
else newAttrs = newAttrs.replace(/lang=["'][^"']*["']/i, `lang="${locale}"`);
|
|
188
|
+
if (!/dir=/i.test(attrs)) newAttrs += ` dir="${dir}"`;
|
|
189
|
+
else newAttrs = newAttrs.replace(/dir=["'][^"']*["']/i, `dir="${dir}"`);
|
|
190
|
+
return `<html${newAttrs}>`;
|
|
191
|
+
});
|
|
192
|
+
html = html.replace(/(<script\s+[^>]*type=["']module["'][^>]*src=["'])([^"']*)(["'])/gi, (m, prefix, src, suffix) => {
|
|
193
|
+
if (src.includes("node_modules") || src.startsWith("http") || src.startsWith("//")) return m;
|
|
194
|
+
return `${prefix}${src}${src.includes("?") ? "&" : "?"}zintl-multiplex=${locale}${suffix}`;
|
|
195
|
+
});
|
|
196
|
+
res.statusCode = 200;
|
|
197
|
+
res.setHeader("Content-Type", "text/html");
|
|
198
|
+
return res.end(await server.transformIndexHtml(url, html));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (pathname === "/" || pathname === "/index.html") {
|
|
202
|
+
const defaultLocale = ctx.options.sourceLocale;
|
|
203
|
+
const localesStr = JSON.stringify(locales);
|
|
204
|
+
const redirectHtml = `
|
|
205
|
+
<!doctype html>
|
|
206
|
+
<html>
|
|
207
|
+
<head>
|
|
208
|
+
<script>
|
|
209
|
+
(function() {
|
|
210
|
+
try {
|
|
211
|
+
const lang = (navigator.language || '${defaultLocale}').split('-')[0];
|
|
212
|
+
const supported = ${localesStr};
|
|
213
|
+
const parts = window.location.pathname.split('/').filter(Boolean);
|
|
214
|
+
if (parts.length > 0 && supported.includes(parts[0])) return;
|
|
215
|
+
const target = supported.includes(lang) ? lang : '${defaultLocale}';
|
|
216
|
+
const path = window.location.pathname.replace(/^\\/+/, '');
|
|
217
|
+
window.location.replace('/' + target + '/' + path + window.location.search + window.location.hash);
|
|
218
|
+
} catch (e) {
|
|
219
|
+
const supported = ${localesStr};
|
|
220
|
+
const parts = window.location.pathname.split('/').filter(Boolean);
|
|
221
|
+
if (parts.length > 0 && supported.includes(parts[0])) return;
|
|
222
|
+
const path = window.location.pathname.replace(/^\\/+/, '');
|
|
223
|
+
window.location.replace('/${defaultLocale}/' + path);
|
|
224
|
+
}
|
|
225
|
+
})();
|
|
226
|
+
<\/script>
|
|
227
|
+
</head>
|
|
228
|
+
</html>`;
|
|
229
|
+
res.statusCode = 200;
|
|
230
|
+
res.setHeader("Content-Type", "text/html");
|
|
231
|
+
return res.end(redirectHtml);
|
|
232
|
+
}
|
|
233
|
+
next();
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
//#endregion
|
|
239
|
+
//#region src/hooks/resolve.ts
|
|
240
|
+
function injectMultiplexQuery(id, locale) {
|
|
241
|
+
const parts = id.split("?");
|
|
242
|
+
const cleanId = parts[0];
|
|
243
|
+
if (parts.length === 1) return `${cleanId}?zintl-multiplex=${locale}`;
|
|
244
|
+
const params = parts[1].split("&").filter((p) => !p.startsWith("zintl-multiplex="));
|
|
245
|
+
if (params.length === 0) return `${cleanId}?zintl-multiplex=${locale}`;
|
|
246
|
+
const lastParam = params[params.length - 1];
|
|
247
|
+
if (lastParam && /\.(ts|tsx|js|jsx|mts|mjs|css|scss|less|html|vue|svelte)$/i.test(lastParam)) {
|
|
248
|
+
const before = params.slice(0, -1);
|
|
249
|
+
return `${cleanId}?${before.length ? before.join("&") + "&" : ""}zintl-multiplex=${locale}&${lastParam}`;
|
|
250
|
+
}
|
|
251
|
+
return `${cleanId}?${params.join("&")}&zintl-multiplex=${locale}`;
|
|
252
|
+
}
|
|
253
|
+
function resolveIdHook(ctx) {
|
|
254
|
+
return async function(id, importer, options) {
|
|
255
|
+
const isSsr = this.environment ? this.environment.config.consumer === "server" : !!options?.ssr;
|
|
256
|
+
if (id.includes(".zintl-")) {
|
|
257
|
+
const cleanId = id.split("?")[0];
|
|
258
|
+
if (isAbsolute(cleanId)) return id;
|
|
259
|
+
}
|
|
260
|
+
if (id.startsWith(".") && importer && importer.includes(".zintl-")) {
|
|
261
|
+
const cleanId = id.split("?")[0];
|
|
262
|
+
id = join(dirname(importer.split("?")[0]), cleanId) + (id.includes("?") ? "?" + id.split("?")[1] : "");
|
|
263
|
+
}
|
|
264
|
+
if (id === "virtual:zintl/runtime" || id === "virtual:zintl/runtime/internal" || id.startsWith("virtual:zintl/runtime/")) return "\0" + id;
|
|
265
|
+
if (importer && importer.includes("virtual:zintl/runtime")) {
|
|
266
|
+
if (id.startsWith("./")) return "\0virtual:zintl/runtime/" + id.replace("./", "").replace(".js", "").replace(".mjs", "");
|
|
267
|
+
}
|
|
268
|
+
if (id.startsWith("virtual:zintl-catalog") || id.startsWith("virtual:zintl/catalog") || id.startsWith("virtual:zintl/content") || id.startsWith("virtual:zintl/manager")) {
|
|
269
|
+
ctx.compiler._logger.withPrefix("Vite").debug(`Resolving virtual module: ${id}`);
|
|
270
|
+
return "\0" + id;
|
|
271
|
+
}
|
|
272
|
+
const cleanId = id.split("?")[0];
|
|
273
|
+
if (ctx.compiler.assets.isSupportedAsset(cleanId)) {
|
|
274
|
+
let absolutePath = cleanId;
|
|
275
|
+
if (cleanId.startsWith(".") && importer) absolutePath = join(dirname(importer.split("?")[0]), cleanId);
|
|
276
|
+
await ctx.compiler.assets.registerAsset(absolutePath);
|
|
277
|
+
}
|
|
278
|
+
if (id.includes("zintl-multiplex=")) {
|
|
279
|
+
const cleanId = id.split("?")[0];
|
|
280
|
+
if (ctx.compiler.assets.isSupportedAsset(cleanId)) {
|
|
281
|
+
const locale = ctx.getMultiplexLocale(id);
|
|
282
|
+
if (locale) {
|
|
283
|
+
let absolutePath = cleanId;
|
|
284
|
+
if (cleanId.startsWith(".") && importer) absolutePath = join(dirname(importer.split("?")[0]), cleanId);
|
|
285
|
+
await ctx.compiler.assets.registerAsset(absolutePath);
|
|
286
|
+
const cleanQueries = (id.split("?")[1] || "").split("&").filter((q) => !q.startsWith("zintl-multiplex=")).join("&");
|
|
287
|
+
const suffix = cleanQueries ? `?${cleanQueries}` : "";
|
|
288
|
+
if (locale === ctx.compiler.sourceLocale) return absolutePath + suffix;
|
|
289
|
+
const assetId = ctx.compiler.getNormalizedId(absolutePath);
|
|
290
|
+
if (ctx.options.virtualAssets) return `\0virtual:zintl/asset/${locale}/${assetId}${suffix}`;
|
|
291
|
+
const localizedPath = ctx.compiler.assets.getAssetPath(assetId, locale);
|
|
292
|
+
if (existsSync(localizedPath)) return localizedPath + suffix;
|
|
293
|
+
return absolutePath + suffix;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (ctx.getMultiplex() && id.endsWith(".html")) {
|
|
298
|
+
const locales = ctx.options.locales;
|
|
299
|
+
for (const loc of locales) {
|
|
300
|
+
const pattern = new RegExp(`(^|\\/)${loc}\\/([^\\/]+\\.html)$`);
|
|
301
|
+
if (pattern.test(id)) {
|
|
302
|
+
const match = id.match(pattern);
|
|
303
|
+
if (match) return join(ctx.compiler.rootDir, `${loc}/${match[2]}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (importer) {
|
|
308
|
+
const locale = ctx.getMultiplexLocale(importer);
|
|
309
|
+
if (locale && !id.includes("zintl-multiplex=") && !id.includes(".zintl-")) {
|
|
310
|
+
const cleanId = id.split("?")[0];
|
|
311
|
+
const extMatch = cleanId.match(/\.([a-zA-Z0-9]+)$/);
|
|
312
|
+
const ext = extMatch ? extMatch[1].toLowerCase() : "";
|
|
313
|
+
if (!ext || [
|
|
314
|
+
"js",
|
|
315
|
+
"jsx",
|
|
316
|
+
"ts",
|
|
317
|
+
"tsx",
|
|
318
|
+
"md",
|
|
319
|
+
"txt",
|
|
320
|
+
"vue",
|
|
321
|
+
"svelte"
|
|
322
|
+
].includes(ext) || ctx.compiler.assets.isSupportedAsset(cleanId)) {
|
|
323
|
+
const resolvedClean = await this.resolve(id, importer, {
|
|
324
|
+
skipSelf: true,
|
|
325
|
+
ssr: isSsr
|
|
326
|
+
});
|
|
327
|
+
if (resolvedClean) {
|
|
328
|
+
const cleanResolvedId = (typeof resolvedClean === "string" ? resolvedClean : resolvedClean.id).split("?")[0];
|
|
329
|
+
let isTranslationNeutral = false;
|
|
330
|
+
if (ctx.compiler?.messages?.metadataGraph) {
|
|
331
|
+
const startFileId = ctx.compiler.getNormalizedId(cleanResolvedId);
|
|
332
|
+
const hasActiveZintlTransitive = (fileId, visited = /* @__PURE__ */ new Set()) => {
|
|
333
|
+
if (typeof fileId !== "string" || visited.has(fileId)) return false;
|
|
334
|
+
visited.add(fileId);
|
|
335
|
+
const cleanFileId = fileId.split("?")[0];
|
|
336
|
+
return (() => {
|
|
337
|
+
if (ctx.compiler.assets.isSupportedAsset(cleanFileId)) return true;
|
|
338
|
+
if ((ctx.compiler?.assets?.getRegisteredAssets() || []).some((asset) => asset === cleanFileId || asset.startsWith(cleanFileId + "."))) return true;
|
|
339
|
+
const meta = ctx.compiler.messages.metadataGraph[cleanFileId];
|
|
340
|
+
if (meta) {
|
|
341
|
+
if (meta.hasZintlMarker || meta.hasZintlMacro || meta.anchorSites && meta.anchorSites.length > 0 || meta.needsLoader) return true;
|
|
342
|
+
}
|
|
343
|
+
const manifestKeys = Object.keys(ctx.compiler?.messages?.internalManifest || {});
|
|
344
|
+
for (const key of manifestKeys) if (key === cleanFileId || key.startsWith(cleanFileId + ":")) {
|
|
345
|
+
const msgs = ctx.compiler.messages.internalManifest[key];
|
|
346
|
+
if (msgs && msgs.length > 0) return true;
|
|
347
|
+
}
|
|
348
|
+
const deps = ctx.compiler.messages.dependencyGraph[cleanFileId];
|
|
349
|
+
if (deps) for (const dep of deps) {
|
|
350
|
+
const depId = typeof dep === "string" ? dep : dep?.id;
|
|
351
|
+
if (depId) {
|
|
352
|
+
const depFileId = depId.startsWith(".") ? join(dirname(cleanFileId), depId) : depId;
|
|
353
|
+
const nDepId = ctx.compiler.getNormalizedId(depFileId);
|
|
354
|
+
if (hasActiveZintlTransitive(nDepId, visited)) return true;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return false;
|
|
358
|
+
})();
|
|
359
|
+
};
|
|
360
|
+
isTranslationNeutral = !hasActiveZintlTransitive(startFileId);
|
|
361
|
+
}
|
|
362
|
+
if (isTranslationNeutral) return resolvedClean;
|
|
363
|
+
}
|
|
364
|
+
if (ext === "vue" || ext === "svelte") {
|
|
365
|
+
const resolved = await this.resolve(id, importer, {
|
|
366
|
+
skipSelf: true,
|
|
367
|
+
ssr: isSsr
|
|
368
|
+
});
|
|
369
|
+
if (resolved) {
|
|
370
|
+
const resolvedId = typeof resolved === "string" ? resolved : resolved.id;
|
|
371
|
+
const cleanResolvedId = resolvedId.split("?")[0];
|
|
372
|
+
const suffix = resolvedId.includes("?") ? "?" + resolvedId.split("?")[1] : "";
|
|
373
|
+
const finalId = cleanResolvedId.replace(/\.(vue|svelte)$/, `.zintl-${locale}.$1`) + suffix;
|
|
374
|
+
if (typeof resolved === "string") return finalId;
|
|
375
|
+
return {
|
|
376
|
+
...resolved,
|
|
377
|
+
id: finalId
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
} else {
|
|
381
|
+
const newId = injectMultiplexQuery(id, locale);
|
|
382
|
+
const resolved = await this.resolve(newId, importer, {
|
|
383
|
+
skipSelf: true,
|
|
384
|
+
ssr: isSsr
|
|
385
|
+
});
|
|
386
|
+
if (resolved) {
|
|
387
|
+
const resolvedId = typeof resolved === "string" ? resolved : resolved.id;
|
|
388
|
+
const cleanResolvedId = resolvedId.split("?")[0];
|
|
389
|
+
if (ctx.compiler.assets.isSupportedAsset(cleanResolvedId)) {
|
|
390
|
+
await ctx.compiler.assets.registerAsset(cleanResolvedId);
|
|
391
|
+
if (locale === ctx.compiler.sourceLocale) return resolved;
|
|
392
|
+
const assetId = ctx.compiler.getNormalizedId(cleanResolvedId);
|
|
393
|
+
const queries = resolvedId.split("?")[1] || "";
|
|
394
|
+
const suffix = queries ? `?${queries}` : "";
|
|
395
|
+
if (ctx.options.virtualAssets) {
|
|
396
|
+
const finalId = `\0virtual:zintl/asset/${locale}/${assetId}${suffix}`;
|
|
397
|
+
if (typeof resolved === "string") return finalId;
|
|
398
|
+
return {
|
|
399
|
+
...resolved,
|
|
400
|
+
id: finalId
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
const localizedPath = ctx.compiler.assets.getAssetPath(assetId, locale);
|
|
404
|
+
if (existsSync(localizedPath)) {
|
|
405
|
+
const finalId = localizedPath + suffix;
|
|
406
|
+
if (typeof resolved === "string") return finalId;
|
|
407
|
+
return {
|
|
408
|
+
...resolved,
|
|
409
|
+
id: finalId
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
let finalResolvedId = resolvedId;
|
|
414
|
+
if (!finalResolvedId.includes("zintl-multiplex=")) {
|
|
415
|
+
finalResolvedId = injectMultiplexQuery(resolvedId, locale);
|
|
416
|
+
if (typeof resolved === "string") return finalResolvedId;
|
|
417
|
+
return {
|
|
418
|
+
...resolved,
|
|
419
|
+
id: finalResolvedId
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
return resolved;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function loadHook(ctx) {
|
|
431
|
+
return async function(id, options) {
|
|
432
|
+
const isSsr = this.environment ? this.environment.config.consumer === "server" : !!options?.ssr;
|
|
433
|
+
const cleanId = id.split("?")[0];
|
|
434
|
+
if (cleanId.startsWith("\0virtual:zintl/asset/")) {
|
|
435
|
+
const rest = cleanId.slice(21);
|
|
436
|
+
const slashIdx = rest.indexOf("/");
|
|
437
|
+
if (slashIdx !== -1) {
|
|
438
|
+
const locale = rest.slice(0, slashIdx);
|
|
439
|
+
const assetId = rest.slice(slashIdx + 1);
|
|
440
|
+
const originalPath = join(ctx.compiler.rootDir, assetId);
|
|
441
|
+
if (existsSync(originalPath)) {
|
|
442
|
+
this.addWatchFile(originalPath);
|
|
443
|
+
if (ctx.compiler.assets.resolveAssetConfig(assetId)?.strategy === "binary-passthrough") {
|
|
444
|
+
const sourceBuffer = readFileSync(originalPath);
|
|
445
|
+
const sourceHashKey = `@zintl/asset-hash:${sha1(sourceBuffer)}`;
|
|
446
|
+
const hive = ctx.compiler.messages.hive;
|
|
447
|
+
const sourceLocale = ctx.options.sourceLocale;
|
|
448
|
+
let buffer = sourceBuffer;
|
|
449
|
+
if (locale !== sourceLocale) {
|
|
450
|
+
const hiveBackup = hive?.[locale]?.[sourceHashKey];
|
|
451
|
+
if (hiveBackup) buffer = Buffer.from(hiveBackup, "base64");
|
|
452
|
+
}
|
|
453
|
+
return `export default import.meta.ROLLUP_FILE_URL_${this.emitFile({
|
|
454
|
+
type: "asset",
|
|
455
|
+
name: assetId.split("/").pop(),
|
|
456
|
+
source: buffer
|
|
457
|
+
})};`;
|
|
458
|
+
} else {
|
|
459
|
+
const content = (await ctx.compiler.assets.getAssetTranslations(locale))[`@zintl/asset:${assetId}`] ?? readFileSync(originalPath, "utf-8");
|
|
460
|
+
if (id.includes("?raw") || id.includes("?zintl-raw")) {
|
|
461
|
+
let code = `export default ${JSON.stringify(content)};`;
|
|
462
|
+
if (ctx.compiler.isDev) code += "\nif (import.meta.hot) { import.meta.hot.accept(); }";
|
|
463
|
+
return code;
|
|
464
|
+
}
|
|
465
|
+
return `export default import.meta.ROLLUP_FILE_URL_${this.emitFile({
|
|
466
|
+
type: "asset",
|
|
467
|
+
name: assetId.split("/").pop(),
|
|
468
|
+
source: content
|
|
469
|
+
})};`;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (cleanId.includes(".zintl-") && !id.includes("?vue") && !id.includes("&vue") && !id.includes("?svelte") && !id.includes("&svelte")) {
|
|
475
|
+
const originalPath = cleanId.replace(/\.zintl-[a-zA-Z0-9_-]+\.(vue|svelte)/, ".$1");
|
|
476
|
+
if (existsSync(originalPath)) {
|
|
477
|
+
const content = readFileSync(originalPath, "utf-8");
|
|
478
|
+
this.addWatchFile(originalPath);
|
|
479
|
+
return content;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
if (cleanId.startsWith("\0virtual:zintl/runtime")) {
|
|
483
|
+
const moduleName = cleanId.replace("\0virtual:zintl/runtime/", "").replace("\0virtual:zintl/runtime", "internal");
|
|
484
|
+
ctx.compiler._logger.withPrefix("Vite").debug(`Loading virtual runtime module: ${moduleName}`);
|
|
485
|
+
let code = getRuntimeCode(moduleName, ctx.compiler._resolved.flags, isSsr, ctx.compiler.sourceLocale);
|
|
486
|
+
if (!isSsr) code = code.replace(/await\s+import\(\s*["']node:async_hooks["']\s*\)/g, "null");
|
|
487
|
+
return code;
|
|
488
|
+
}
|
|
489
|
+
if (cleanId.endsWith(".md") || cleanId.endsWith(".txt")) {
|
|
490
|
+
if (existsSync(cleanId)) {
|
|
491
|
+
const content = readFileSync(cleanId, "utf-8");
|
|
492
|
+
const ext = cleanId.endsWith(".md") ? ".md" : ".txt";
|
|
493
|
+
const translationOnly = ctx.compiler.assets.getTranslationOnly(content, ext);
|
|
494
|
+
this.addWatchFile(cleanId);
|
|
495
|
+
if (id.includes("?zintl-raw")) {
|
|
496
|
+
let code = `export default ${JSON.stringify(translationOnly)};`;
|
|
497
|
+
if (ctx.compiler.isDev) code += "\nif (import.meta.hot) { import.meta.hot.accept(); }";
|
|
498
|
+
return code;
|
|
499
|
+
}
|
|
500
|
+
const assetId = ctx.compiler.getNormalizedId(cleanId);
|
|
501
|
+
await ctx.compiler.assets.registerAsset(cleanId);
|
|
502
|
+
if (id.includes("?raw")) {
|
|
503
|
+
const multiplexLocale = ctx.getMultiplexLocale(id);
|
|
504
|
+
const sourceLocale = ctx.options.sourceLocale;
|
|
505
|
+
if (multiplexLocale) {
|
|
506
|
+
const localizedPath = multiplexLocale === sourceLocale ? cleanId : ctx.compiler.assets.getAssetPath(assetId, multiplexLocale);
|
|
507
|
+
const content = existsSync(localizedPath) ? readFileSync(localizedPath, "utf-8") : translationOnly;
|
|
508
|
+
let code = `export default ${JSON.stringify(content)};`;
|
|
509
|
+
if (ctx.compiler.isDev) code += "\nif (import.meta.hot) { import.meta.hot.accept(); }";
|
|
510
|
+
return code;
|
|
511
|
+
}
|
|
512
|
+
const locales = ctx.options.locales;
|
|
513
|
+
for (const loc of locales) {
|
|
514
|
+
if (loc === sourceLocale) continue;
|
|
515
|
+
const localizedPath = ctx.compiler.assets.getAssetPath(assetId, loc);
|
|
516
|
+
if (existsSync(localizedPath)) this.addWatchFile(localizedPath);
|
|
517
|
+
}
|
|
518
|
+
const rawAssetKey = `@zintl/asset:${assetId}`;
|
|
519
|
+
const assetKey = ctx.compiler.isDev ? rawAssetKey : generateMessageId(rawAssetKey);
|
|
520
|
+
return `
|
|
521
|
+
import { getLocale, _t } from "virtual:zintl/runtime/internal";
|
|
522
|
+
const sourceContent = ${JSON.stringify(translationOnly)};
|
|
523
|
+
const assetKey = ${JSON.stringify(assetKey)};
|
|
524
|
+
const proxy = new Proxy({}, {
|
|
525
|
+
get(target, prop) {
|
|
526
|
+
const multiplexLocale = ${JSON.stringify(ctx.getMultiplexLocale(id) || null)};
|
|
527
|
+
const loc = getLocale() || multiplexLocale || ${JSON.stringify(sourceLocale)};
|
|
528
|
+
const val = loc === ${JSON.stringify(sourceLocale)}
|
|
529
|
+
? sourceContent
|
|
530
|
+
: (_t(assetKey, {}, { _bId: "b_assets" }) || sourceContent);
|
|
531
|
+
if (prop === Symbol.toPrimitive) {
|
|
532
|
+
return () => val;
|
|
533
|
+
}
|
|
534
|
+
if (prop === "toString" || prop === "valueOf") {
|
|
535
|
+
return () => val;
|
|
536
|
+
}
|
|
537
|
+
if (prop === "toJSON") {
|
|
538
|
+
return () => val;
|
|
539
|
+
}
|
|
540
|
+
if (typeof val[prop] === "function") {
|
|
541
|
+
return val[prop].bind(val);
|
|
542
|
+
}
|
|
543
|
+
return val[prop];
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
export default proxy;
|
|
547
|
+
if (import.meta.hot) {
|
|
548
|
+
import.meta.hot.accept();
|
|
549
|
+
}
|
|
550
|
+
`;
|
|
551
|
+
}
|
|
552
|
+
return translationOnly;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
if (ctx.getMultiplex() && id.endsWith(".html")) {
|
|
556
|
+
const locales = ctx.options.locales;
|
|
557
|
+
let isFanned = false;
|
|
558
|
+
let matchedLocale = "";
|
|
559
|
+
let originalHtmlFile = "";
|
|
560
|
+
for (const loc of locales) {
|
|
561
|
+
const pattern = new RegExp(`[\\/\\\\]${loc}[\\/\\\\]([^\\/\\\\]+\\.html)$`);
|
|
562
|
+
const m = id.match(pattern);
|
|
563
|
+
if (m) {
|
|
564
|
+
isFanned = true;
|
|
565
|
+
matchedLocale = loc;
|
|
566
|
+
originalHtmlFile = m[1];
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (isFanned) {
|
|
571
|
+
const originalPath = join(ctx.compiler.rootDir, originalHtmlFile);
|
|
572
|
+
if (existsSync(originalPath)) {
|
|
573
|
+
let html = readFileSync(originalPath, "utf-8");
|
|
574
|
+
let dir = matchedLocale === "ar" ? "rtl" : "ltr";
|
|
575
|
+
try {
|
|
576
|
+
const catalogPath = ctx.compiler.html.getCatalogPath(originalHtmlFile, matchedLocale);
|
|
577
|
+
if (existsSync(catalogPath)) {
|
|
578
|
+
const cat = JSON.parse(readFileSync(catalogPath, "utf-8"));
|
|
579
|
+
const catalogDir = ctx.compiler.isMultilingualFormat() ? cat.dir?.[matchedLocale] : cat.dir;
|
|
580
|
+
if (catalogDir !== void 0) dir = catalogDir;
|
|
581
|
+
}
|
|
582
|
+
} catch {}
|
|
583
|
+
html = html.replace(/<html([^>]*)>/i, (m, attrs) => {
|
|
584
|
+
let newAttrs = attrs;
|
|
585
|
+
if (!/lang=/i.test(attrs)) newAttrs += ` lang="${matchedLocale}"`;
|
|
586
|
+
else newAttrs = newAttrs.replace(/lang=["'][^"']*["']/i, `lang="${matchedLocale}"`);
|
|
587
|
+
if (!/dir=/i.test(attrs)) newAttrs += ` dir="${dir}"`;
|
|
588
|
+
else newAttrs = newAttrs.replace(/dir=["'][^"']*["']/i, `dir="${dir}"`);
|
|
589
|
+
return `<html${newAttrs}>`;
|
|
590
|
+
});
|
|
591
|
+
html = html.replace(/(<script\s+[^>]*type=["']module["'][^>]*src=["'])([^"']*)(["'])/gi, (m, prefix, src, suffix) => {
|
|
592
|
+
if (src.includes("node_modules") || src.startsWith("http") || src.startsWith("//")) return m;
|
|
593
|
+
return `${prefix}${injectMultiplexQuery(src, matchedLocale)}${suffix}`;
|
|
594
|
+
});
|
|
595
|
+
return html;
|
|
596
|
+
}
|
|
597
|
+
} else if (!id.replace(/\\/g, "/").includes("node_modules")) {
|
|
598
|
+
if (existsSync(id)) {
|
|
599
|
+
let html = readFileSync(id, "utf-8");
|
|
600
|
+
html = html.replace(/<script[^>]*src=["'][^"']*(src\/main\.ts|main\.ts)["'][^>]*>([\s\S]*?)<\/script>/gi, "");
|
|
601
|
+
return html;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
const vLogger = ctx.compiler._logger.withPrefix("Vite");
|
|
606
|
+
if (cleanId.startsWith("\0virtual:zintl-catalog")) {
|
|
607
|
+
const boundaryId = cleanId.slice(23);
|
|
608
|
+
vLogger.debug(`Loading legacy virtual catalog: ${boundaryId}`);
|
|
609
|
+
const { code, watchedFiles } = await ctx.compiler.generateVirtualModule(boundaryId);
|
|
610
|
+
for (const path of watchedFiles) this.addWatchFile(path);
|
|
611
|
+
return code;
|
|
612
|
+
}
|
|
613
|
+
if (cleanId.startsWith("\0virtual:zintl/catalog")) {
|
|
614
|
+
const prefix = CHUNK_VIRTUAL_PREFIX;
|
|
615
|
+
const match = cleanId.slice(1).match(new RegExp(`^${prefix}/([^:]+):(.+)$`));
|
|
616
|
+
if (match) {
|
|
617
|
+
const [, chunkType, chunkId] = match;
|
|
618
|
+
vLogger.debug(`Loading chunk [${chunkType}]: ${chunkId}`);
|
|
619
|
+
const fullModuleId = `${chunkType}:${chunkId}`;
|
|
620
|
+
const { code, watchedFiles } = await ctx.compiler.generateVirtualModule(fullModuleId);
|
|
621
|
+
for (const path of watchedFiles) this.addWatchFile(path);
|
|
622
|
+
return code;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (cleanId.startsWith("\0virtual:zintl/content")) {
|
|
626
|
+
const prefix = CONTENT_VIRTUAL_PREFIX;
|
|
627
|
+
const match = cleanId.slice(1).match(new RegExp(`^${prefix}/([^/]+)/([^:]+):(.+)$`));
|
|
628
|
+
if (match) {
|
|
629
|
+
const [, locale, chunkType, chunkId] = match;
|
|
630
|
+
vLogger.debug(`Loading content [${locale}] for [${chunkType}]: ${chunkId}`);
|
|
631
|
+
const fullModuleId = `${chunkType}:${chunkId}`;
|
|
632
|
+
const { code, watchedFiles } = await ctx.compiler.generateVirtualModule(fullModuleId, locale);
|
|
633
|
+
for (const path of watchedFiles) this.addWatchFile(path);
|
|
634
|
+
return code;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
if (cleanId.startsWith("\0virtual:zintl/manager")) {
|
|
638
|
+
const prefix = MANAGER_VIRTUAL_PREFIX;
|
|
639
|
+
const match = cleanId.slice(1).match(new RegExp(`^${prefix}/([^/]+)/([^:]+):(.+)$`));
|
|
640
|
+
if (match) {
|
|
641
|
+
const [, syncLocale, chunkType, chunkId] = match;
|
|
642
|
+
const locale = syncLocale === "none" ? void 0 : syncLocale;
|
|
643
|
+
vLogger.debug(`Loading manager [${syncLocale}] for [${chunkType}]: ${chunkId}`);
|
|
644
|
+
const fullModuleId = `${chunkType}:${chunkId}`;
|
|
645
|
+
const { code, watchedFiles } = await ctx.compiler.generateVirtualModule(fullModuleId, locale, true);
|
|
646
|
+
for (const path of watchedFiles) this.addWatchFile(path);
|
|
647
|
+
return code;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
//#endregion
|
|
653
|
+
//#region src/hooks/transform.ts
|
|
654
|
+
function transformHook(ctx) {
|
|
655
|
+
return async function(code, id, options) {
|
|
656
|
+
const isSsr = this && this.environment ? this.environment.config.consumer === "server" : !!options?.ssr;
|
|
657
|
+
const vLogger = ctx.compiler._logger.withPrefix("Vite");
|
|
658
|
+
if (ctx.server && !ctx.discovered) {
|
|
659
|
+
ctx.discovered = true;
|
|
660
|
+
try {
|
|
661
|
+
await ctx.compiler.discover();
|
|
662
|
+
} catch (err) {
|
|
663
|
+
if (err.code !== "ENOENT") throw err;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
const isTargetSsrEntry = ctx.compiler?.isSsrEntryTarget?.(id);
|
|
667
|
+
if (id.includes("node_modules") && !isTargetSsrEntry || id.startsWith("\0") && !isTargetSsrEntry || id.includes("?vue") || id.includes("&vue") || id.includes("?svelte") || id.includes("&svelte") || id.includes("?") && !id.includes("zintl-multiplex=")) return;
|
|
668
|
+
const multiplexLocale = ctx.getMultiplexLocale(id);
|
|
669
|
+
const cleanId = id.split("?")[0];
|
|
670
|
+
const result = await ctx.compiler.transform(code, cleanId, VIRTUAL_PREFIX, false, multiplexLocale, isSsr);
|
|
671
|
+
const mg = this && this.environment ? this.environment.moduleGraph : ctx.server?.moduleGraph;
|
|
672
|
+
if (mg && !id.startsWith("\0")) {
|
|
673
|
+
const boundaryId = ctx.compiler.getNormalizedId(id);
|
|
674
|
+
const affectedChunkIds = ctx.compiler.getAffectedChunks(boundaryId);
|
|
675
|
+
if (affectedChunkIds.length > 0) {
|
|
676
|
+
vLogger.debug(`Invalidating ${affectedChunkIds.length} affected chunks for ${boundaryId}`);
|
|
677
|
+
for (const chunkModuleId of affectedChunkIds) for (const [modId, mod] of mg.idToModuleMap) if (modId.includes(chunkModuleId) && modId.includes("virtual:zintl")) {
|
|
678
|
+
vLogger.debug(`[HMR] Invalidating virtual module: ${modId}`);
|
|
679
|
+
mg.invalidateModule(mod);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
return result;
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
function transformIndexHtmlHook(ctx) {
|
|
687
|
+
return {
|
|
688
|
+
order: "post",
|
|
689
|
+
async handler(html, viteCtx) {
|
|
690
|
+
let htmlId = viteCtx.filename || viteCtx.path || "";
|
|
691
|
+
if (viteCtx.path) {
|
|
692
|
+
const pathParts = viteCtx.path.split("/").filter(Boolean);
|
|
693
|
+
const locales = ctx.options.locales;
|
|
694
|
+
const foundLocale = pathParts.find((p) => locales.includes(p));
|
|
695
|
+
if (foundLocale) {
|
|
696
|
+
const pathName = pathParts.filter((p) => p !== foundLocale).join("/");
|
|
697
|
+
const baseName = pathName ? pathName.endsWith(".html") ? pathName : `${pathName}/index.html` : "index.html";
|
|
698
|
+
htmlId = join(dirname(htmlId), foundLocale, baseName);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
const normalizedPath = htmlId.replace(/\\/g, "/");
|
|
702
|
+
const parts = normalizedPath.split("?")[0].split("/");
|
|
703
|
+
const locales = ctx.options.locales;
|
|
704
|
+
const isFanned = parts.some((p) => locales.includes(p)) || normalizedPath.includes("virtual:zintl-multiplex-html");
|
|
705
|
+
if (ctx.getMultiplex() && !isFanned) return html;
|
|
706
|
+
const preloads = {};
|
|
707
|
+
const base = viteCtx.server?.config?.base || "";
|
|
708
|
+
if (viteCtx.bundle) {
|
|
709
|
+
for (const [fileName, chunk] of Object.entries(viteCtx.bundle)) if (chunk.type === "chunk") for (const modId of chunk.moduleIds) {
|
|
710
|
+
const match = modId.match(/virtual:zintl\/content\/([^/]+)\//);
|
|
711
|
+
if (match) {
|
|
712
|
+
const locale = match[1];
|
|
713
|
+
if (!preloads[locale]) preloads[locale] = [];
|
|
714
|
+
const url = `${base}${fileName}`;
|
|
715
|
+
if (!preloads[locale].includes(url)) preloads[locale].push(url);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
return await ctx.compiler.transformHtml(html, htmlId, preloads);
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
function preTransformIndexHtmlHook(ctx) {
|
|
724
|
+
return {
|
|
725
|
+
order: "pre",
|
|
726
|
+
handler(html, viteCtx) {
|
|
727
|
+
const normalizedPath = (viteCtx.filename || viteCtx.path || "").replace(/\\/g, "/");
|
|
728
|
+
const parts = normalizedPath.split("?")[0].split("/");
|
|
729
|
+
const locales = ctx.options.locales;
|
|
730
|
+
const pathParts = (viteCtx.path || "").split("/").filter(Boolean);
|
|
731
|
+
const isFanned = parts.some((p) => locales.includes(p)) || pathParts.some((p) => locales.includes(p)) || normalizedPath.includes("virtual:zintl-multiplex-html");
|
|
732
|
+
if (ctx.getMultiplex() && !isFanned) {
|
|
733
|
+
const localesStr = JSON.stringify(locales);
|
|
734
|
+
const defaultLocale = ctx.options.sourceLocale;
|
|
735
|
+
return `<!doctype html>
|
|
736
|
+
<html lang="${defaultLocale}">
|
|
737
|
+
<head>
|
|
738
|
+
<meta charset="UTF-8" />
|
|
739
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
740
|
+
<title>Redirecting...</title>
|
|
741
|
+
<script id="zintl-multiplex-redirect">
|
|
742
|
+
(function() {
|
|
743
|
+
try {
|
|
744
|
+
const lang = (navigator.language || '${defaultLocale}').split('-')[0];
|
|
745
|
+
const supported = ${localesStr};
|
|
746
|
+
const parts = window.location.pathname.split('/').filter(Boolean);
|
|
747
|
+
if (parts.length > 0 && supported.includes(parts[0])) return;
|
|
748
|
+
const target = supported.includes(lang) ? lang : '${defaultLocale}';
|
|
749
|
+
const path = window.location.pathname.replace(/^/+/, '');
|
|
750
|
+
window.location.replace('/' + target + '/' + path + window.location.search + window.location.hash);
|
|
751
|
+
} catch (e) {
|
|
752
|
+
const supported = ${localesStr};
|
|
753
|
+
const parts = window.location.pathname.split('/').filter(Boolean);
|
|
754
|
+
if (parts.length > 0 && supported.includes(parts[0])) return;
|
|
755
|
+
const path = window.location.pathname.replace(/^/+/, '');
|
|
756
|
+
window.location.replace('/${defaultLocale}/' + path);
|
|
757
|
+
}
|
|
758
|
+
})();
|
|
759
|
+
<\/script>
|
|
760
|
+
</head>
|
|
761
|
+
<body>
|
|
762
|
+
</body>
|
|
763
|
+
</html>`;
|
|
764
|
+
}
|
|
765
|
+
return html;
|
|
766
|
+
}
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
//#endregion
|
|
770
|
+
//#region src/hooks/hmr.ts
|
|
771
|
+
function handleHotUpdateHook(ctx) {
|
|
772
|
+
return async function({ file, server, modules, timestamp }) {
|
|
773
|
+
const vLogger = ctx.compiler._logger.withPrefix("Vite");
|
|
774
|
+
if (ctx.compiler.isWritingFile(file)) return;
|
|
775
|
+
const isSource = /\.(ts|tsx|js|jsx|html|vue|svelte)$/.test(file);
|
|
776
|
+
const isJson = file.endsWith(".json");
|
|
777
|
+
const isAsset = file.endsWith(".md") || file.endsWith(".txt");
|
|
778
|
+
if (!isJson && !isSource && !isAsset) return;
|
|
779
|
+
vLogger.debug(`HMR triggered for ${file}`);
|
|
780
|
+
const invalidatedModules = /* @__PURE__ */ new Set();
|
|
781
|
+
let invalidatedBoundaries = [];
|
|
782
|
+
const mg = this && this.environment ? this.environment.moduleGraph : server.moduleGraph;
|
|
783
|
+
const invalidate = (mod) => {
|
|
784
|
+
mg.invalidateModule(mod);
|
|
785
|
+
if (timestamp !== void 0) mod.lastHMRTimestamp = timestamp;
|
|
786
|
+
invalidatedModules.add(mod);
|
|
787
|
+
};
|
|
788
|
+
if (isJson || isAsset) {
|
|
789
|
+
if (isAsset) await ctx.compiler.assets.registerAsset(file);
|
|
790
|
+
const inv = await ctx.compiler.invalidateFile(file, true);
|
|
791
|
+
for (const b of inv) invalidatedBoundaries.push(b);
|
|
792
|
+
if (inv.length === 0 && isJson) {
|
|
793
|
+
for (const [id, mod] of mg.idToModuleMap) if (id.includes("virtual:zintl") && id.includes("/manager/")) invalidate(mod);
|
|
794
|
+
}
|
|
795
|
+
} else {
|
|
796
|
+
const inv = await ctx.compiler.invalidateFile(file);
|
|
797
|
+
for (const b of inv) invalidatedBoundaries.push(b);
|
|
798
|
+
}
|
|
799
|
+
ctx.compiler.flush().catch((e) => vLogger.error(`Background flush failed: ${String(e)}`));
|
|
800
|
+
for (const mod of modules) {
|
|
801
|
+
const isBaseJson = isJson && mod.file === file;
|
|
802
|
+
const isBaseAsset = isAsset && mod.file === file && !mod.id?.includes("?");
|
|
803
|
+
if (isBaseJson || isBaseAsset) continue;
|
|
804
|
+
invalidate(mod);
|
|
805
|
+
if (!isSource && mod.importers) {
|
|
806
|
+
for (const importer of mod.importers) if (importer.id && !importer.id.includes("node_modules")) invalidate(importer);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
const boundaryIds = new Set(invalidatedBoundaries);
|
|
810
|
+
if (isSource) boundaryIds.add(ctx.compiler.getNormalizedId(file));
|
|
811
|
+
for (const boundaryId of boundaryIds) {
|
|
812
|
+
if (boundaryId === "b_assets" && ctx.compiler.graph.boundaryGraph) {
|
|
813
|
+
for (const [_nid, n] of ctx.compiler.graph.boundaryGraph.nodes.entries()) if (n.mode === "entry" && n.filePath && n.filePath !== "assets") {
|
|
814
|
+
const relPath = n.filePath.startsWith("/") ? n.filePath : "/" + n.filePath;
|
|
815
|
+
for (const [id, mod] of mg.idToModuleMap) {
|
|
816
|
+
const normalizedId = id.split("?")[0];
|
|
817
|
+
const idNoExt = normalizedId.replace(/\.[a-z0-9]+$/i, "");
|
|
818
|
+
if (id === relPath || id === n.filePath || normalizedId.endsWith(n.filePath) || idNoExt.endsWith(n.filePath)) invalidate(mod);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
const affectedChunkIds = ctx.compiler.getAffectedChunks(boundaryId);
|
|
823
|
+
for (const chunkModuleId of affectedChunkIds) for (const [id, mod] of mg.idToModuleMap) if (id.includes(chunkModuleId) && id.includes("virtual:zintl")) invalidate(mod);
|
|
824
|
+
let node = ctx.compiler.graph.boundaryGraph?.nodes.get(boundaryId);
|
|
825
|
+
if (!node) {
|
|
826
|
+
for (const [_nid, n] of ctx.compiler.graph.boundaryGraph?.nodes.entries() || []) if (ctx.compiler.io.getSafeBoundaryId(_nid) === boundaryId) {
|
|
827
|
+
node = n;
|
|
828
|
+
break;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
const fileId = node?.filePath || (boundaryId.includes(":") ? boundaryId.split(":")[0] : boundaryId);
|
|
832
|
+
if (fileId && fileId !== "assets" && !fileId.includes("\0")) {
|
|
833
|
+
const absFileId = isAbsolute(fileId) ? fileId : join(ctx.compiler.rootDir, fileId);
|
|
834
|
+
if (typeof mg.getModulesByFile === "function") {
|
|
835
|
+
const sourceMods = mg.getModulesByFile(absFileId);
|
|
836
|
+
if (sourceMods && (sourceMods instanceof Set ? sourceMods.size > 0 : Array.isArray(sourceMods) && sourceMods.length > 0)) for (const mod of sourceMods) invalidate(mod);
|
|
837
|
+
}
|
|
838
|
+
const virtualId = `${RESOLVED_VIRTUAL_PREFIX}:${fileId}`;
|
|
839
|
+
if (typeof mg.getModuleById === "function") {
|
|
840
|
+
const vMod = mg.getModuleById(virtualId);
|
|
841
|
+
if (vMod) invalidate(vMod);
|
|
842
|
+
}
|
|
843
|
+
const relPath = fileId.startsWith("/") ? fileId : "/" + fileId;
|
|
844
|
+
const fileIdNoExt = fileId.replace(/\.[a-z0-9]+$/i, "");
|
|
845
|
+
const relPathNoExt = relPath.replace(/\.[a-z0-9]+$/i, "");
|
|
846
|
+
for (const [id, mod] of mg.idToModuleMap) {
|
|
847
|
+
const cleanModuleId = id.replace(/\.zintl-[a-zA-Z0-9_-]+\.(vue|svelte)/, ".$1");
|
|
848
|
+
const normalizedId = cleanModuleId.split("?")[0];
|
|
849
|
+
const normalizedIdNoExt = normalizedId.replace(/\.[a-z0-9]+$/i, "");
|
|
850
|
+
if ((cleanModuleId === relPath || cleanModuleId === fileId || cleanModuleId.endsWith(fileId) || normalizedId.endsWith(fileId) || normalizedId === relPath || normalizedId === fileId || normalizedIdNoExt === fileIdNoExt || normalizedIdNoExt === relPathNoExt || normalizedIdNoExt.endsWith(fileIdNoExt)) && !id.includes("virtual:zintl")) {
|
|
851
|
+
const absFileId = isAbsolute(fileId) ? fileId : join(ctx.compiler.rootDir, fileId);
|
|
852
|
+
if (mod.file !== absFileId) {
|
|
853
|
+
if (mg.fileToModulesMap) {
|
|
854
|
+
const map = mg.fileToModulesMap;
|
|
855
|
+
const hasGet = typeof map.get === "function";
|
|
856
|
+
const hasSet = typeof map.set === "function";
|
|
857
|
+
if (mod.file) if (hasGet) {
|
|
858
|
+
const oldSet = map.get(mod.file);
|
|
859
|
+
if (oldSet) oldSet.delete(mod);
|
|
860
|
+
} else {
|
|
861
|
+
const oldSet = map[mod.file];
|
|
862
|
+
if (oldSet) oldSet.delete(mod);
|
|
863
|
+
}
|
|
864
|
+
if (hasGet && hasSet) {
|
|
865
|
+
let set = map.get(absFileId);
|
|
866
|
+
if (!set) {
|
|
867
|
+
set = /* @__PURE__ */ new Set();
|
|
868
|
+
map.set(absFileId, set);
|
|
869
|
+
}
|
|
870
|
+
set.add(mod);
|
|
871
|
+
} else if (!hasGet && !hasSet) {
|
|
872
|
+
let set = map[absFileId];
|
|
873
|
+
if (!set) {
|
|
874
|
+
set = /* @__PURE__ */ new Set();
|
|
875
|
+
map[absFileId] = set;
|
|
876
|
+
}
|
|
877
|
+
set.add(mod);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
mod.file = absFileId;
|
|
881
|
+
}
|
|
882
|
+
invalidate(mod);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
if (boundaryId.endsWith(".html")) server.ws.send({
|
|
887
|
+
type: "full-reload",
|
|
888
|
+
path: "*"
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
let hasServerOnlyUpdate = false;
|
|
892
|
+
for (const boundaryId of boundaryIds) {
|
|
893
|
+
const isSsr = ctx.compiler.ssrBoundaries?.has(boundaryId);
|
|
894
|
+
const isClient = ctx.compiler.clientBoundaries?.has(boundaryId);
|
|
895
|
+
if (isSsr && !isClient) {
|
|
896
|
+
hasServerOnlyUpdate = true;
|
|
897
|
+
break;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
if (hasServerOnlyUpdate) {
|
|
901
|
+
vLogger.debug("Server-only boundary update detected, triggering full-reload");
|
|
902
|
+
server.ws.send({
|
|
903
|
+
type: "full-reload",
|
|
904
|
+
path: "*"
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
if (invalidatedModules.size > 0) return Array.from(invalidatedModules);
|
|
908
|
+
return modules;
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
//#endregion
|
|
912
|
+
//#region src/hooks/build.ts
|
|
913
|
+
function buildStartHook(ctx) {
|
|
914
|
+
return async function() {
|
|
915
|
+
ctx.compiler._logger.withPrefix("Vite").debug("Build starting...");
|
|
916
|
+
await ctx.compiler.setup();
|
|
917
|
+
if (!ctx.server) {
|
|
918
|
+
await ctx.compiler.discover();
|
|
919
|
+
ctx.discovered = true;
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
function buildEndHook(ctx) {
|
|
924
|
+
return async function() {
|
|
925
|
+
await ctx.compiler.flush();
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
//#endregion
|
|
929
|
+
//#region src/options.ts
|
|
930
|
+
/**
|
|
931
|
+
* Every context-free default, in one table.
|
|
932
|
+
*
|
|
933
|
+
* `outputDir`, `catalogFormat`, `metadataDir` and `similarityThreshold` are
|
|
934
|
+
* intentionally absent: the compiler owns those defaults (`DEFAULT_OUTPUT_DIR`,
|
|
935
|
+
* the catalog format fallback, `node_modules/.zintl`, `DEFAULT_RENAME_THRESHOLD`)
|
|
936
|
+
* and re-stating them here would recreate the duplication this file exists to
|
|
937
|
+
* remove. Leaving them `undefined` lets the compiler apply its own.
|
|
938
|
+
*/
|
|
939
|
+
const DEFAULTS = {
|
|
940
|
+
sourceLocale: "en",
|
|
941
|
+
locales: ["en"],
|
|
942
|
+
prune: true,
|
|
943
|
+
debug: false,
|
|
944
|
+
virtualAssets: false,
|
|
945
|
+
facets: ["auto"],
|
|
946
|
+
/** `undefined` → auto-detect by scanning entry files for `zintl()` / `zintl("*")`. */
|
|
947
|
+
multiplex: void 0,
|
|
948
|
+
/** `undefined` → `true` for `vite build`, `false` for `vite serve`. */
|
|
949
|
+
verifyIntegrity: void 0,
|
|
950
|
+
/** `undefined` → fall back to Vite's own `logLevel`, then `"info"`. */
|
|
951
|
+
logLevel: void 0
|
|
952
|
+
};
|
|
953
|
+
function resolveOptions(options) {
|
|
954
|
+
const o = options ?? {};
|
|
955
|
+
return {
|
|
956
|
+
...o,
|
|
957
|
+
sourceLocale: o.sourceLocale ?? DEFAULTS.sourceLocale,
|
|
958
|
+
locales: o.locales ?? [...DEFAULTS.locales],
|
|
959
|
+
prune: o.prune ?? DEFAULTS.prune,
|
|
960
|
+
debug: o.debug ?? DEFAULTS.debug,
|
|
961
|
+
virtualAssets: o.virtualAssets ?? DEFAULTS.virtualAssets,
|
|
962
|
+
facets: o.facets ?? [...DEFAULTS.facets]
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
//#endregion
|
|
966
|
+
//#region src/plugin.ts
|
|
967
|
+
const contextMap = /* @__PURE__ */ new WeakMap();
|
|
968
|
+
//#endregion
|
|
969
|
+
//#region src/vite.ts
|
|
970
|
+
/**
|
|
971
|
+
* @module zintl/vite
|
|
972
|
+
*
|
|
973
|
+
* The Vite plugin, and the public types for configuring it.
|
|
974
|
+
*/
|
|
975
|
+
/**
|
|
976
|
+
* The Zintl Vite plugin.
|
|
977
|
+
*
|
|
978
|
+
* Add it once, and Zintl takes over internationalization from there: it
|
|
979
|
+
* extracts the strings reachable from every `zintl()` anchor, writes a catalog
|
|
980
|
+
* per locale into {@link Options.outputDir | `outputDir`}, splits those catalogs
|
|
981
|
+
* along your bundler's own code-splitting boundaries, and rewrites the macros
|
|
982
|
+
* into a runtime with no message parser shipped to the client.
|
|
983
|
+
*
|
|
984
|
+
* Returns two Vite plugins — a `pre` pass that localizes HTML entries before
|
|
985
|
+
* anything else touches them, and the main `zintl` plugin. Pass the array
|
|
986
|
+
* straight to `plugins`; Vite flattens it.
|
|
987
|
+
*
|
|
988
|
+
* Catalogs are written for you but owned by you: edit them, commit them, hand
|
|
989
|
+
* them to translators. Renamed and lightly edited source strings carry their
|
|
990
|
+
* translations forward instead of resetting (see
|
|
991
|
+
* {@link Options.similarityThreshold | `similarityThreshold`}).
|
|
992
|
+
*
|
|
993
|
+
* @example
|
|
994
|
+
* ```ts
|
|
995
|
+
* // vite.config.ts
|
|
996
|
+
* import { defineConfig } from "vite";
|
|
997
|
+
* import zintl from "zintljs/vite";
|
|
998
|
+
*
|
|
999
|
+
* export default defineConfig({
|
|
1000
|
+
* plugins: [
|
|
1001
|
+
* zintl({
|
|
1002
|
+
* sourceLocale: "en",
|
|
1003
|
+
* locales: ["en", "ar", "fr"],
|
|
1004
|
+
* outputDir: "./src/locales",
|
|
1005
|
+
* }),
|
|
1006
|
+
* ],
|
|
1007
|
+
* });
|
|
1008
|
+
* ```
|
|
1009
|
+
*
|
|
1010
|
+
* @see {@link Options} for every option and its default.
|
|
1011
|
+
*/
|
|
1012
|
+
const vite = createUnplugin((options) => {
|
|
1013
|
+
const resolved = resolveOptions(options);
|
|
1014
|
+
const ctx = new Context(resolved);
|
|
1015
|
+
contextMap.set(resolved, ctx);
|
|
1016
|
+
if (typeof globalThis !== "undefined") {
|
|
1017
|
+
const activeContexts = globalThis;
|
|
1018
|
+
activeContexts.__zintl_active_contexts = activeContexts.__zintl_active_contexts || [];
|
|
1019
|
+
activeContexts.__zintl_active_contexts.push(ctx);
|
|
1020
|
+
}
|
|
1021
|
+
return [{
|
|
1022
|
+
name: "zintl-pre",
|
|
1023
|
+
vite: {
|
|
1024
|
+
enforce: "pre",
|
|
1025
|
+
transformIndexHtml: preTransformIndexHtmlHook(ctx)
|
|
1026
|
+
}
|
|
1027
|
+
}, {
|
|
1028
|
+
name: PLUGIN_NAME,
|
|
1029
|
+
enforce: "pre",
|
|
1030
|
+
buildStart() {
|
|
1031
|
+
return buildStartHook(ctx).call(this);
|
|
1032
|
+
},
|
|
1033
|
+
resolveId(id, importer, options) {
|
|
1034
|
+
return resolveIdHook(ctx).call(this, id, importer, options);
|
|
1035
|
+
},
|
|
1036
|
+
load(id) {
|
|
1037
|
+
return loadHook(ctx).call(this, id);
|
|
1038
|
+
},
|
|
1039
|
+
transform(code, id) {
|
|
1040
|
+
return transformHook(ctx).call(this, code, id);
|
|
1041
|
+
},
|
|
1042
|
+
buildEnd() {
|
|
1043
|
+
return buildEndHook(ctx).call(this);
|
|
1044
|
+
},
|
|
1045
|
+
vite: {
|
|
1046
|
+
config: configHook(ctx),
|
|
1047
|
+
configResolved: configResolvedHook(ctx),
|
|
1048
|
+
configureServer: configureServerHook(ctx),
|
|
1049
|
+
transformIndexHtml: transformIndexHtmlHook(ctx),
|
|
1050
|
+
handleHotUpdate: handleHotUpdateHook(ctx),
|
|
1051
|
+
hotUpdate: handleHotUpdateHook(ctx)
|
|
1052
|
+
}
|
|
1053
|
+
}];
|
|
1054
|
+
}).vite;
|
|
1055
|
+
//#endregion
|
|
1056
|
+
export { vite as default, vite as "module.exports" };
|