cloudflare-next-intl 0.10.27 → 0.10.29
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 +21 -0
- package/dist/src/client_messages_scan/index.d.ts +3 -0
- package/dist/src/client_messages_scan/index.js +3 -0
- package/dist/src/client_messages_scan/match_namespace.d.ts +1 -0
- package/dist/src/client_messages_scan/match_namespace.js +3 -0
- package/dist/src/client_messages_scan/scan_file.d.ts +16 -0
- package/dist/src/client_messages_scan/scan_file.js +175 -0
- package/dist/src/client_messages_scan/scan_project.d.ts +16 -0
- package/dist/src/client_messages_scan/scan_project.js +67 -0
- package/dist/src/cookie_consent/client/components/auto_analytics_events.js +1 -1
- package/dist/src/cookie_consent/index.d.ts +1 -0
- package/dist/src/cookie_consent/index.js +1 -0
- package/dist/src/general/pick_client_messages.d.ts +2 -0
- package/dist/src/general/pick_client_messages.js +17 -0
- package/dist/src/image_optimizer/next_image_shim.js +10 -1
- package/dist/src/server/components/server_provider.js +2 -1
- package/dist/src/server/components/server_provider_static.js +2 -1
- package/dist/src/types/types.d.ts +1 -0
- package/dist/src/vite/auto_client_messages_plugin.d.ts +7 -0
- package/dist/src/vite/auto_client_messages_plugin.js +39 -0
- package/dist/src/vite/auto_image_loader_plugin.d.ts +21 -0
- package/dist/src/vite/auto_image_loader_plugin.js +151 -0
- package/dist/src/vite/index.d.ts +2 -0
- package/dist/src/vite/index.js +2 -0
- package/dist/src/vite/plugin.d.ts +4 -0
- package/dist/src/vite/plugin.js +12 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -166,6 +166,24 @@ export function Navigation() {
|
|
|
166
166
|
}
|
|
167
167
|
```
|
|
168
168
|
|
|
169
|
+
`IntlProvider` sends the locale's messages to the client for `useTranslations`
|
|
170
|
+
in client components. By default, `clientMessages: "auto"` and `autoClientMessages`
|
|
171
|
+
Vite plugin are **enabled by default** with `{ onDynamic: "fallback", fallbackNamespaces: [] }`
|
|
172
|
+
to automatically scan and ship only the namespaces your client components use.
|
|
173
|
+
|
|
174
|
+
You can customize or disable either side:
|
|
175
|
+
- Set `clientMessages: false` (server-only messages), `true` (all messages), or `["Auth", "Common", "Categories*"]` in intl config.
|
|
176
|
+
- In `vite.config.ts`, pass `autoClientMessages: false` to disable scanning, or customize options:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
// vite.config.ts (optional customization — enabled by default)
|
|
180
|
+
cloudflareNextIntl({
|
|
181
|
+
autoClientMessages: { fallbackNamespaces: ["Common"], packages: ["@my/ui"] }
|
|
182
|
+
})
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
> UI library packages can declare `"i18n": { "namespaces": ["Buttons"] }` in `package.json` for automatic inclusion.
|
|
186
|
+
|
|
169
187
|
### Locale-aware links (non-locale-switching)
|
|
170
188
|
|
|
171
189
|
Use `Link` (server-safe, from `./Link`) for normal navigation that should
|
|
@@ -609,6 +627,9 @@ import { useCookieConsent } from "cloudflare-next-intl/useCookieConsent";
|
|
|
609
627
|
const { consent, setConsent } = useCookieConsent();
|
|
610
628
|
```
|
|
611
629
|
|
|
630
|
+
Composing the providers yourself (`autoWireAnalytics: false`)? `AutoAnalyticsEvents`
|
|
631
|
+
is exported from `cloudflare-next-intl/AutoAnalyticsEvents` (and `cloudflare-next-intl/cookieConsent`).
|
|
632
|
+
|
|
612
633
|
See [`package/src/cookie_consent/README.md`](package/src/cookie_consent/README.md) for layout, customization, and gotchas.
|
|
613
634
|
|
|
614
635
|
### Error handling
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function matchesNamespacePattern(ns: string, pattern: string): boolean;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface DynamicCall {
|
|
2
|
+
file: string;
|
|
3
|
+
line: number;
|
|
4
|
+
text: string;
|
|
5
|
+
prop?: {
|
|
6
|
+
component: string;
|
|
7
|
+
name: string;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export interface FileScanResult {
|
|
11
|
+
namespaces: string[];
|
|
12
|
+
dynamicCalls: DynamicCall[];
|
|
13
|
+
usesRoot: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function findJsxPropValues(file: string, code: string, component: string, prop: string): string[] | null;
|
|
16
|
+
export declare function scanFile(file: string, code: string): FileScanResult;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
const HOOK_MODULES = new Set(["cloudflare-next-intl/use", "cloudflare-next-intl/client", "cloudflare-next-intl"]);
|
|
3
|
+
function topLevel(ns) {
|
|
4
|
+
return ns.split(".")[0];
|
|
5
|
+
}
|
|
6
|
+
function literalsOfType(node, sf, aliases) {
|
|
7
|
+
if (!node)
|
|
8
|
+
return null;
|
|
9
|
+
if (ts.isLiteralTypeNode(node) && ts.isStringLiteral(node.literal))
|
|
10
|
+
return [node.literal.text];
|
|
11
|
+
if (ts.isUnionTypeNode(node)) {
|
|
12
|
+
const out = [];
|
|
13
|
+
for (const t of node.types) {
|
|
14
|
+
const r = literalsOfType(t, sf, aliases);
|
|
15
|
+
if (!r)
|
|
16
|
+
return null;
|
|
17
|
+
out.push(...r);
|
|
18
|
+
}
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName))
|
|
22
|
+
return literalsOfType(aliases.get(node.typeName.text), sf, aliases);
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
function componentName(fn) {
|
|
26
|
+
if ((ts.isFunctionDeclaration(fn) || ts.isFunctionExpression(fn)) && fn.name)
|
|
27
|
+
return fn.name.text;
|
|
28
|
+
if ((ts.isArrowFunction(fn) || ts.isFunctionExpression(fn)) && ts.isVariableDeclaration(fn.parent) && ts.isIdentifier(fn.parent.name))
|
|
29
|
+
return fn.parent.name.text;
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
function propOf(arg, propParams) {
|
|
33
|
+
if (ts.isIdentifier(arg)) {
|
|
34
|
+
const p = propParams.get(arg.text);
|
|
35
|
+
return p ?? null;
|
|
36
|
+
}
|
|
37
|
+
if (ts.isTemplateExpression(arg) && arg.head.text === "" && ts.isIdentifier(arg.templateSpans[0].expression)) {
|
|
38
|
+
const p = propParams.get(arg.templateSpans[0].expression.text);
|
|
39
|
+
return p ?? null;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
export function findJsxPropValues(file, code, component, prop) {
|
|
44
|
+
if (!code.includes(`<${component}`))
|
|
45
|
+
return [];
|
|
46
|
+
const sf = ts.createSourceFile(file, code, ts.ScriptTarget.Latest, true, file.endsWith("x") ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
|
|
47
|
+
const values = [];
|
|
48
|
+
let unresolved = false;
|
|
49
|
+
const visit = (node) => {
|
|
50
|
+
if ((ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) && node.tagName.getText() === component) {
|
|
51
|
+
let value = null;
|
|
52
|
+
for (const attr of node.attributes.properties) {
|
|
53
|
+
if (ts.isJsxSpreadAttribute(attr)) {
|
|
54
|
+
value = null;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
if (attr.name.getText() !== prop)
|
|
58
|
+
continue;
|
|
59
|
+
const init = attr.initializer;
|
|
60
|
+
if (init && ts.isStringLiteral(init))
|
|
61
|
+
value = init.text;
|
|
62
|
+
else if (init && ts.isJsxExpression(init) && init.expression && (ts.isStringLiteral(init.expression) || ts.isNoSubstitutionTemplateLiteral(init.expression)))
|
|
63
|
+
value = init.expression.text;
|
|
64
|
+
}
|
|
65
|
+
if (value === null)
|
|
66
|
+
unresolved = true;
|
|
67
|
+
else
|
|
68
|
+
values.push(value);
|
|
69
|
+
}
|
|
70
|
+
ts.forEachChild(node, visit);
|
|
71
|
+
};
|
|
72
|
+
visit(sf);
|
|
73
|
+
return unresolved ? null : values;
|
|
74
|
+
}
|
|
75
|
+
export function scanFile(file, code) {
|
|
76
|
+
const result = { namespaces: [], dynamicCalls: [], usesRoot: false };
|
|
77
|
+
if (!code.includes("useTranslations"))
|
|
78
|
+
return result;
|
|
79
|
+
const sf = ts.createSourceFile(file, code, ts.ScriptTarget.Latest, true, file.endsWith("x") ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
|
|
80
|
+
const hookNames = new Set();
|
|
81
|
+
const consts = new Map();
|
|
82
|
+
const typeAliases = new Map();
|
|
83
|
+
const paramTypes = new Map();
|
|
84
|
+
const propParams = new Map();
|
|
85
|
+
const collect = (node) => {
|
|
86
|
+
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier) && HOOK_MODULES.has(node.moduleSpecifier.text)) {
|
|
87
|
+
const named = node.importClause?.namedBindings;
|
|
88
|
+
if (named && ts.isNamedImports(named)) {
|
|
89
|
+
for (const el of named.elements)
|
|
90
|
+
if ((el.propertyName ?? el.name).text === "useTranslations")
|
|
91
|
+
hookNames.add(el.name.text);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer)
|
|
95
|
+
consts.set(node.name.text, node.initializer);
|
|
96
|
+
if (ts.isTypeAliasDeclaration(node))
|
|
97
|
+
typeAliases.set(node.name.text, node.type);
|
|
98
|
+
if (ts.isParameter(node) && node.type) {
|
|
99
|
+
if (ts.isIdentifier(node.name))
|
|
100
|
+
paramTypes.set(node.name.text, node.type);
|
|
101
|
+
const component = componentName(node.parent);
|
|
102
|
+
if (component && ts.isObjectBindingPattern(node.name)) {
|
|
103
|
+
for (const el of node.name.elements) {
|
|
104
|
+
if (!ts.isIdentifier(el.name))
|
|
105
|
+
continue;
|
|
106
|
+
const key = (el.propertyName && ts.isIdentifier(el.propertyName) ? el.propertyName : el.name).text;
|
|
107
|
+
propParams.set(el.name.text, { component, name: key });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (ts.isObjectBindingPattern(node.name) && ts.isTypeLiteralNode(node.type)) {
|
|
111
|
+
for (const el of node.name.elements) {
|
|
112
|
+
if (!ts.isIdentifier(el.name))
|
|
113
|
+
continue;
|
|
114
|
+
const key = (el.propertyName && ts.isIdentifier(el.propertyName) ? el.propertyName : el.name).text;
|
|
115
|
+
const member = node.type.members.find((m) => ts.isPropertySignature(m) && ts.isIdentifier(m.name) && m.name.text === key);
|
|
116
|
+
if (member?.type)
|
|
117
|
+
paramTypes.set(el.name.text, member.type);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
ts.forEachChild(node, collect);
|
|
122
|
+
};
|
|
123
|
+
collect(sf);
|
|
124
|
+
if (hookNames.size === 0)
|
|
125
|
+
return result;
|
|
126
|
+
const resolve = (expr, depth = 0) => {
|
|
127
|
+
if (depth > 5)
|
|
128
|
+
return null;
|
|
129
|
+
if (ts.isStringLiteral(expr) || ts.isNoSubstitutionTemplateLiteral(expr))
|
|
130
|
+
return [expr.text];
|
|
131
|
+
if (ts.isTemplateExpression(expr) && expr.head.text.includes(".")) {
|
|
132
|
+
const top = expr.head.text.split(".")[0];
|
|
133
|
+
if (top)
|
|
134
|
+
return [top];
|
|
135
|
+
}
|
|
136
|
+
if (ts.isParenthesizedExpression(expr) || ts.isAsExpression(expr))
|
|
137
|
+
return resolve(expr.expression, depth + 1);
|
|
138
|
+
if (ts.isConditionalExpression(expr)) {
|
|
139
|
+
const a = resolve(expr.whenTrue, depth + 1);
|
|
140
|
+
const b = resolve(expr.whenFalse, depth + 1);
|
|
141
|
+
return a && b ? [...a, ...b] : null;
|
|
142
|
+
}
|
|
143
|
+
if (ts.isIdentifier(expr)) {
|
|
144
|
+
const init = consts.get(expr.text);
|
|
145
|
+
if (init)
|
|
146
|
+
return resolve(init, depth + 1);
|
|
147
|
+
return literalsOfType(paramTypes.get(expr.text), sf, typeAliases);
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
};
|
|
151
|
+
const found = new Set();
|
|
152
|
+
const visit = (node) => {
|
|
153
|
+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && hookNames.has(node.expression.text)) {
|
|
154
|
+
const arg = node.arguments[0];
|
|
155
|
+
if (!arg)
|
|
156
|
+
result.usesRoot = true;
|
|
157
|
+
else {
|
|
158
|
+
const values = resolve(arg);
|
|
159
|
+
if (values)
|
|
160
|
+
values.forEach((v) => found.add(topLevel(v)));
|
|
161
|
+
else {
|
|
162
|
+
const call = { file, line: sf.getLineAndCharacterOfPosition(arg.getStart()).line + 1, text: arg.getText() };
|
|
163
|
+
const prop = propOf(arg, propParams);
|
|
164
|
+
if (prop)
|
|
165
|
+
call.prop = prop;
|
|
166
|
+
result.dynamicCalls.push(call);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
ts.forEachChild(node, visit);
|
|
171
|
+
};
|
|
172
|
+
visit(sf);
|
|
173
|
+
result.namespaces = [...found];
|
|
174
|
+
return result;
|
|
175
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { matchesNamespacePattern } from "./match_namespace.js";
|
|
2
|
+
import { type DynamicCall } from "./scan_file.js";
|
|
3
|
+
export { matchesNamespacePattern };
|
|
4
|
+
export interface ScanProjectOptions {
|
|
5
|
+
root: string;
|
|
6
|
+
dirs?: string[];
|
|
7
|
+
packages?: string[];
|
|
8
|
+
fallbackNamespaces?: string[];
|
|
9
|
+
onDynamic?: "all" | "fallback";
|
|
10
|
+
}
|
|
11
|
+
export interface ClientMessagesManifest {
|
|
12
|
+
namespaces: string[] | true;
|
|
13
|
+
dynamicCalls: DynamicCall[];
|
|
14
|
+
scannedFiles: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function scanProject(opts: ScanProjectOptions): ClientMessagesManifest;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { matchesNamespacePattern } from "./match_namespace.js";
|
|
4
|
+
import { findJsxPropValues, scanFile } from "./scan_file.js";
|
|
5
|
+
export { matchesNamespacePattern };
|
|
6
|
+
const EXT = /\.(tsx?|jsx?|mjs)$/;
|
|
7
|
+
const SKIP_DIRS = new Set(["node_modules", "dist", ".next", ".git"]);
|
|
8
|
+
function walk(dir, out, skipNodeModules) {
|
|
9
|
+
for (const name of readdirSync(dir)) {
|
|
10
|
+
const full = join(dir, name);
|
|
11
|
+
if (statSync(full).isDirectory()) {
|
|
12
|
+
if (SKIP_DIRS.has(name) && (skipNodeModules || name !== "dist"))
|
|
13
|
+
continue;
|
|
14
|
+
walk(full, out, skipNodeModules);
|
|
15
|
+
}
|
|
16
|
+
else if (EXT.test(name) && !/\.(test|spec|bench)\./.test(name) && !name.endsWith(".d.ts"))
|
|
17
|
+
out.push(full);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function scanProject(opts) {
|
|
21
|
+
const files = [];
|
|
22
|
+
for (const d of opts.dirs ?? ["src", "app", "components", "pages", "shared", "modules", "lib"]) {
|
|
23
|
+
const full = join(opts.root, d);
|
|
24
|
+
if (existsSync(full))
|
|
25
|
+
walk(full, files, true);
|
|
26
|
+
}
|
|
27
|
+
const found = new Set(opts.fallbackNamespaces ?? []);
|
|
28
|
+
for (const pkg of opts.packages ?? []) {
|
|
29
|
+
const pkgDir = join(opts.root, "node_modules", pkg);
|
|
30
|
+
const pjPath = join(pkgDir, "package.json");
|
|
31
|
+
if (!existsSync(pjPath))
|
|
32
|
+
continue;
|
|
33
|
+
const pj = JSON.parse(readFileSync(pjPath, "utf8"));
|
|
34
|
+
if (Array.isArray(pj.i18n?.namespaces))
|
|
35
|
+
pj.i18n.namespaces.forEach((n) => found.add(n));
|
|
36
|
+
else
|
|
37
|
+
walk(pkgDir, files, false);
|
|
38
|
+
}
|
|
39
|
+
let dynamicCalls = [];
|
|
40
|
+
let usesRoot = false;
|
|
41
|
+
const sources = new Map();
|
|
42
|
+
for (const f of files) {
|
|
43
|
+
const code = readFileSync(f, "utf8");
|
|
44
|
+
sources.set(f, code);
|
|
45
|
+
const r = scanFile(f, code);
|
|
46
|
+
r.namespaces.forEach((n) => found.add(n));
|
|
47
|
+
dynamicCalls.push(...r.dynamicCalls);
|
|
48
|
+
usesRoot || (usesRoot = r.usesRoot);
|
|
49
|
+
}
|
|
50
|
+
dynamicCalls = dynamicCalls.filter((call) => {
|
|
51
|
+
if (!call.prop)
|
|
52
|
+
return true;
|
|
53
|
+
const values = [];
|
|
54
|
+
for (const [f, code] of sources) {
|
|
55
|
+
const v = findJsxPropValues(f, code, call.prop.component, call.prop.name);
|
|
56
|
+
if (v === null)
|
|
57
|
+
return true;
|
|
58
|
+
values.push(...v);
|
|
59
|
+
}
|
|
60
|
+
if (values.length === 0)
|
|
61
|
+
return true;
|
|
62
|
+
values.forEach((v) => found.add(v.split(".")[0]));
|
|
63
|
+
return false;
|
|
64
|
+
});
|
|
65
|
+
const sendAll = usesRoot || (dynamicCalls.length > 0 && (opts.onDynamic ?? "all") === "all");
|
|
66
|
+
return { namespaces: sendAll ? true : [...found].sort(), dynamicCalls, scannedFiles: files.length };
|
|
67
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { usePathname } from 'next/navigation.js';
|
|
3
|
-
import { useReportWebVitals } from 'next/web-vitals';
|
|
3
|
+
import { useReportWebVitals } from 'next/web-vitals.js';
|
|
4
4
|
import { useEffect } from 'react';
|
|
5
5
|
import useCookieConsent from '../use_cookie_consent.js';
|
|
6
6
|
const metricEventNames = {
|
|
@@ -3,6 +3,7 @@ export { default as useCookieConsent } from './client/use_cookie_consent.js';
|
|
|
3
3
|
export { default as CookieConsentDialog } from './client/components/cookie_consent_dialog.js';
|
|
4
4
|
export { default as PrivacyPolicyUpdateDialog } from './client/components/privacy_policy_update_dialog.js';
|
|
5
5
|
export { default as CookieConsentAnalytics } from './client/components/cookie_consent_analytics.js';
|
|
6
|
+
export { default as AutoAnalyticsEvents } from './client/components/auto_analytics_events.js';
|
|
6
7
|
export { defaultGdprCountries } from './gdpr_countries.js';
|
|
7
8
|
export type { CookieConsentContextType, ConsentValue, CookieDialogClassNames, CookieDialogStyles } from './types.js';
|
|
8
9
|
export type { CookieConsentDialogProps } from './client/components/cookie_consent_dialog.js';
|
|
@@ -3,4 +3,5 @@ export { default as useCookieConsent } from './client/use_cookie_consent.js';
|
|
|
3
3
|
export { default as CookieConsentDialog } from './client/components/cookie_consent_dialog.js';
|
|
4
4
|
export { default as PrivacyPolicyUpdateDialog } from './client/components/privacy_policy_update_dialog.js';
|
|
5
5
|
export { default as CookieConsentAnalytics } from './client/components/cookie_consent_analytics.js';
|
|
6
|
+
export { default as AutoAnalyticsEvents } from './client/components/auto_analytics_events.js';
|
|
6
7
|
export { defaultGdprCountries } from './gdpr_countries.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { matchesNamespacePattern } from "../client_messages_scan/match_namespace.js";
|
|
2
|
+
function readAutoManifest() {
|
|
3
|
+
return typeof __CFNI_CLIENT_MESSAGES__ === "undefined" ? undefined : __CFNI_CLIENT_MESSAGES__;
|
|
4
|
+
}
|
|
5
|
+
export default function pickClientMessages(messages, clientMessages = "auto", autoManifest = readAutoManifest()) {
|
|
6
|
+
const list = clientMessages === "auto" ? autoManifest ?? true : clientMessages;
|
|
7
|
+
if (list === true)
|
|
8
|
+
return messages;
|
|
9
|
+
const picked = {};
|
|
10
|
+
if (list === false)
|
|
11
|
+
return picked;
|
|
12
|
+
for (const namespace of Object.keys(messages)) {
|
|
13
|
+
if (list.some((p) => matchesNamespacePattern(namespace, p)))
|
|
14
|
+
picked[namespace] = messages[namespace];
|
|
15
|
+
}
|
|
16
|
+
return picked;
|
|
17
|
+
}
|
|
@@ -22,6 +22,14 @@ catch {
|
|
|
22
22
|
const images = (manifestData && typeof manifestData === "object" && manifestData.images)
|
|
23
23
|
? manifestData.images
|
|
24
24
|
: {};
|
|
25
|
+
let defaultCustomLoader;
|
|
26
|
+
try {
|
|
27
|
+
const loaderMod = (await import("virtual:cloudflare-next-intl-image-loader"));
|
|
28
|
+
defaultCustomLoader = loaderMod.defaultLoader ?? loaderMod.default;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
defaultCustomLoader = undefined;
|
|
32
|
+
}
|
|
25
33
|
function findEntry(srcVal) {
|
|
26
34
|
if (!srcVal)
|
|
27
35
|
return undefined;
|
|
@@ -85,6 +93,7 @@ function resolveProps(props) {
|
|
|
85
93
|
let width = props.width;
|
|
86
94
|
let height = props.height;
|
|
87
95
|
let style = props.style;
|
|
96
|
+
const loader = props.loader ?? defaultCustomLoader;
|
|
88
97
|
const entry = findEntry(src);
|
|
89
98
|
if (entry) {
|
|
90
99
|
const variant = pickVariant(entry, typeof props.width === "number" ? props.width : undefined);
|
|
@@ -117,7 +126,7 @@ function resolveProps(props) {
|
|
|
117
126
|
...style,
|
|
118
127
|
};
|
|
119
128
|
}
|
|
120
|
-
return { ...props, src, blurDataURL, width, height, style };
|
|
129
|
+
return { ...props, src, blurDataURL, width, height, style, loader };
|
|
121
130
|
}
|
|
122
131
|
export default function Image(props) {
|
|
123
132
|
const resolved = resolveProps(props);
|
|
@@ -5,6 +5,7 @@ import { Suspense } from "react";
|
|
|
5
5
|
import dynamic from "next/dynamic.js";
|
|
6
6
|
import { localesSet } from "../../config/middleware.js";
|
|
7
7
|
import config from "../../config/intl_config.js";
|
|
8
|
+
import pickClientMessages from "../../general/pick_client_messages.js";
|
|
8
9
|
import installConsoleErrorOverride from "../../error_handling/install_console_error_override.js";
|
|
9
10
|
import reportError from "../../error_handling/report_error.js";
|
|
10
11
|
const LocationzationClientProvider = dynamic(() => import("../../client/components/client_provider.js"));
|
|
@@ -21,7 +22,7 @@ export default async function LocationzationProvider({ language, messages, stati
|
|
|
21
22
|
if (messages) {
|
|
22
23
|
setMessageForLocaleCache(language, messages);
|
|
23
24
|
}
|
|
24
|
-
const messagesValue = messages ?? await getMessage(language);
|
|
25
|
+
const messagesValue = pickClientMessages(messages ?? await getMessage(language), config.clientMessages);
|
|
25
26
|
installConsoleErrorOverride(config);
|
|
26
27
|
let initialAuthUser = null;
|
|
27
28
|
const autoWireClientProvider = config.firebaseAuth?.autoWireClientProvider !== false;
|
|
@@ -5,6 +5,7 @@ import { Suspense } from "react";
|
|
|
5
5
|
import dynamic from "next/dynamic.js";
|
|
6
6
|
import { localesSet } from "../../config/middleware.js";
|
|
7
7
|
import config from "../../config/intl_config.js";
|
|
8
|
+
import pickClientMessages from "../../general/pick_client_messages.js";
|
|
8
9
|
import installConsoleErrorOverride from "../../error_handling/install_console_error_override.js";
|
|
9
10
|
import reportError from "../../error_handling/report_error.js";
|
|
10
11
|
const LocationzationClientProvider = dynamic(() => import("../../client/components/client_provider_static.js"));
|
|
@@ -26,7 +27,7 @@ export default async function LocationzationProvider({ language, messages, child
|
|
|
26
27
|
if (messages) {
|
|
27
28
|
setMessageForLocaleCache(language, messages);
|
|
28
29
|
}
|
|
29
|
-
const messagesValue = messages ?? await getMessage(language);
|
|
30
|
+
const messagesValue = pickClientMessages(messages ?? await getMessage(language), config.clientMessages);
|
|
30
31
|
installConsoleErrorOverride(config);
|
|
31
32
|
let analyticsConfig;
|
|
32
33
|
let requiresConsent = true;
|
|
@@ -19,6 +19,7 @@ export interface RoutingConfig<AppLocales extends Locales, AppLocalePrefixMode e
|
|
|
19
19
|
db?: DbRoutingConfig;
|
|
20
20
|
cookieConsent?: CookieConsentRoutingConfig;
|
|
21
21
|
generate?: GenerateRoutingConfig;
|
|
22
|
+
clientMessages?: boolean | "auto" | readonly string[];
|
|
22
23
|
errorHandling?: ErrorHandlingRoutingConfig;
|
|
23
24
|
link?: LinkRoutingConfig;
|
|
24
25
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
import { type ScanProjectOptions } from "../client_messages_scan/scan_project.js";
|
|
3
|
+
export interface AutoClientMessagesOptions extends Partial<Omit<ScanProjectOptions, "root">> {
|
|
4
|
+
root?: string;
|
|
5
|
+
strict?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function autoClientMessagesPlugin(options?: AutoClientMessagesOptions): Plugin;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { scanProject } from "../client_messages_scan/scan_project.js";
|
|
2
|
+
const KEY = "__CFNI_CLIENT_MESSAGES__";
|
|
3
|
+
const SOURCE = /\.(tsx?|jsx?|mjs)$/;
|
|
4
|
+
export function autoClientMessagesPlugin(options = {}) {
|
|
5
|
+
const opts = {
|
|
6
|
+
onDynamic: "fallback",
|
|
7
|
+
fallbackNamespaces: [],
|
|
8
|
+
...options,
|
|
9
|
+
};
|
|
10
|
+
let last = "";
|
|
11
|
+
const run = () => {
|
|
12
|
+
const manifest = scanProject({ ...opts, root: opts.root ?? process.cwd() });
|
|
13
|
+
for (const d of manifest.dynamicCalls)
|
|
14
|
+
console.warn(`[cloudflare-next-intl] dynamic useTranslations: ${d.file}:${d.line} ${d.text}`);
|
|
15
|
+
if (opts.strict && manifest.dynamicCalls.length > 0)
|
|
16
|
+
throw new Error("[cloudflare-next-intl] Dynamic useTranslations namespaces found. Use literals, union types, or fallbackNamespaces with onDynamic: \"fallback\".");
|
|
17
|
+
console.log(manifest.namespaces === true
|
|
18
|
+
? `[cloudflare-next-intl] client messages: sending all (root or dynamic access) from ${manifest.scannedFiles} files`
|
|
19
|
+
: `[cloudflare-next-intl] client messages: ${manifest.namespaces.length} namespaces from ${manifest.scannedFiles} files`);
|
|
20
|
+
return JSON.stringify(manifest.namespaces);
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
name: "cloudflare-next-intl-auto-client-messages",
|
|
24
|
+
config() {
|
|
25
|
+
last = run();
|
|
26
|
+
return { define: { [KEY]: last } };
|
|
27
|
+
},
|
|
28
|
+
handleHotUpdate({ file, server }) {
|
|
29
|
+
if (!SOURCE.test(file))
|
|
30
|
+
return;
|
|
31
|
+
const next = run();
|
|
32
|
+
if (next === last)
|
|
33
|
+
return;
|
|
34
|
+
last = next;
|
|
35
|
+
server.config.define[KEY] = next;
|
|
36
|
+
server.ws.send({ type: "full-reload" });
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
export declare const VIRTUAL_IMAGE_LOADER_ID = "virtual:cloudflare-next-intl-image-loader";
|
|
3
|
+
export declare const RESOLVED_IMAGE_LOADER_ID: string;
|
|
4
|
+
export declare const DEFAULT_LOADER_CANDIDATES: readonly ["image-loader.ts", "image-loader.js", "image-loader.mjs", "image-loader.cjs", "image-loader.tsx", "image-loader.jsx", "src/image-loader.ts", "src/image-loader.js", "src/image-loader.mjs", "src/image-loader.cjs", "src/image-loader.tsx", "src/image-loader.jsx"];
|
|
5
|
+
export interface AutoImageLoaderOptions {
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
file?: string;
|
|
8
|
+
exportName?: string;
|
|
9
|
+
root?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface DetectedImageLoader {
|
|
12
|
+
exists: boolean;
|
|
13
|
+
isEmpty: boolean;
|
|
14
|
+
loaderPath: string | null;
|
|
15
|
+
exportName: string | null;
|
|
16
|
+
}
|
|
17
|
+
export declare function findExportedLoaderName(code: string, preferredExportName?: string): string | null;
|
|
18
|
+
export declare function detectImageLoader(options?: AutoImageLoaderOptions): DetectedImageLoader;
|
|
19
|
+
export declare function generateLoaderVirtualModule(detected: DetectedImageLoader): string;
|
|
20
|
+
export declare function autoImageLoaderPlugin(options?: AutoImageLoaderOptions): Plugin;
|
|
21
|
+
export default autoImageLoaderPlugin;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { getShimPath, VIRTUAL_IMAGE_SHIM_ID } from "../image_optimizer/plugin.js";
|
|
4
|
+
export const VIRTUAL_IMAGE_LOADER_ID = "virtual:cloudflare-next-intl-image-loader";
|
|
5
|
+
export const RESOLVED_IMAGE_LOADER_ID = "\0" + VIRTUAL_IMAGE_LOADER_ID;
|
|
6
|
+
export const DEFAULT_LOADER_CANDIDATES = [
|
|
7
|
+
"image-loader.ts",
|
|
8
|
+
"image-loader.js",
|
|
9
|
+
"image-loader.mjs",
|
|
10
|
+
"image-loader.cjs",
|
|
11
|
+
"image-loader.tsx",
|
|
12
|
+
"image-loader.jsx",
|
|
13
|
+
"src/image-loader.ts",
|
|
14
|
+
"src/image-loader.js",
|
|
15
|
+
"src/image-loader.mjs",
|
|
16
|
+
"src/image-loader.cjs",
|
|
17
|
+
"src/image-loader.tsx",
|
|
18
|
+
"src/image-loader.jsx",
|
|
19
|
+
];
|
|
20
|
+
export function findExportedLoaderName(code, preferredExportName) {
|
|
21
|
+
const stripped = code
|
|
22
|
+
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
23
|
+
.replace(/\/\/.*/g, "");
|
|
24
|
+
if (preferredExportName) {
|
|
25
|
+
if (preferredExportName === "default") {
|
|
26
|
+
if (/export\s+default\b/.test(stripped) || /export\s*\{[^}]*\bas\s+default\b[^}]*\}/.test(stripped)) {
|
|
27
|
+
return "default";
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const fnRegex = new RegExp(`export\\s+(?:async\\s+)?function\\s+${preferredExportName}\\b`);
|
|
32
|
+
if (fnRegex.test(stripped))
|
|
33
|
+
return preferredExportName;
|
|
34
|
+
const constRegex = new RegExp(`export\\s+(?:const|let|var)\\s+${preferredExportName}\\b`);
|
|
35
|
+
if (constRegex.test(stripped))
|
|
36
|
+
return preferredExportName;
|
|
37
|
+
const reExportRegex = new RegExp(`export\\s*\\{[^}]*\\b(?:as\\s+)?${preferredExportName}\\b[^}]*\\}`);
|
|
38
|
+
if (reExportRegex.test(stripped))
|
|
39
|
+
return preferredExportName;
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
if (/export\s+default\b/.test(stripped) || /export\s*\{[^}]*\bas\s+default\b[^}]*\}/.test(stripped)) {
|
|
43
|
+
return "default";
|
|
44
|
+
}
|
|
45
|
+
const fnMatch = stripped.match(/export\s+(?:async\s+)?function\s+([a-zA-Z0-9_$]+)/);
|
|
46
|
+
if (fnMatch?.[1]) {
|
|
47
|
+
return fnMatch[1];
|
|
48
|
+
}
|
|
49
|
+
const varMatch = stripped.match(/export\s+(?:const|let|var)\s+([a-zA-Z0-9_$]+)/);
|
|
50
|
+
if (varMatch?.[1]) {
|
|
51
|
+
return varMatch[1];
|
|
52
|
+
}
|
|
53
|
+
const exportBlockMatch = stripped.match(/export\s*\{([^}]+)\}/);
|
|
54
|
+
if (exportBlockMatch?.[1]) {
|
|
55
|
+
const entries = exportBlockMatch[1].split(",");
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
const parts = entry.trim().split(/\s+as\s+/);
|
|
58
|
+
const exportedName = parts[parts.length - 1]?.trim();
|
|
59
|
+
if (exportedName) {
|
|
60
|
+
return exportedName;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
export function detectImageLoader(options) {
|
|
67
|
+
const root = options?.root ? path.resolve(options.root) : process.cwd();
|
|
68
|
+
let targetPath = null;
|
|
69
|
+
if (options?.file) {
|
|
70
|
+
const customPath = path.isAbsolute(options.file) ? options.file : path.resolve(root, options.file);
|
|
71
|
+
if (existsSync(customPath)) {
|
|
72
|
+
targetPath = customPath;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
return { exists: false, isEmpty: false, loaderPath: null, exportName: null };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
for (const candidate of DEFAULT_LOADER_CANDIDATES) {
|
|
80
|
+
const candidatePath = path.resolve(root, candidate);
|
|
81
|
+
if (existsSync(candidatePath)) {
|
|
82
|
+
targetPath = candidatePath;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (!targetPath) {
|
|
88
|
+
return { exists: false, isEmpty: false, loaderPath: null, exportName: null };
|
|
89
|
+
}
|
|
90
|
+
const content = readFileSync(targetPath, "utf8");
|
|
91
|
+
const stripped = content.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*/g, "").trim();
|
|
92
|
+
if (!stripped) {
|
|
93
|
+
return { exists: true, isEmpty: true, loaderPath: targetPath, exportName: null };
|
|
94
|
+
}
|
|
95
|
+
const exportName = findExportedLoaderName(content, options?.exportName);
|
|
96
|
+
return {
|
|
97
|
+
exists: true,
|
|
98
|
+
isEmpty: false,
|
|
99
|
+
loaderPath: targetPath,
|
|
100
|
+
exportName,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export function generateLoaderVirtualModule(detected) {
|
|
104
|
+
if (!detected.exists || detected.isEmpty || !detected.loaderPath || !detected.exportName) {
|
|
105
|
+
return `export const defaultLoader = undefined;\nexport const hasCustomLoader = false;\nexport default undefined;\n`;
|
|
106
|
+
}
|
|
107
|
+
const normalizedPath = detected.loaderPath.replace(/\\/g, "/");
|
|
108
|
+
if (detected.exportName === "default") {
|
|
109
|
+
return `import loader from ${JSON.stringify(normalizedPath)};\nexport const defaultLoader = loader;\nexport const hasCustomLoader = true;\nexport default loader;\n`;
|
|
110
|
+
}
|
|
111
|
+
return `import { ${detected.exportName} as loader } from ${JSON.stringify(normalizedPath)};\nexport const defaultLoader = loader;\nexport const hasCustomLoader = true;\nexport default loader;\n`;
|
|
112
|
+
}
|
|
113
|
+
export function autoImageLoaderPlugin(options) {
|
|
114
|
+
const isEnabled = options?.enabled !== false;
|
|
115
|
+
return {
|
|
116
|
+
name: "cloudflare-next-intl-auto-image-loader",
|
|
117
|
+
enforce: "pre",
|
|
118
|
+
resolveId(id) {
|
|
119
|
+
if (id === VIRTUAL_IMAGE_LOADER_ID) {
|
|
120
|
+
return RESOLVED_IMAGE_LOADER_ID;
|
|
121
|
+
}
|
|
122
|
+
if (id === VIRTUAL_IMAGE_SHIM_ID) {
|
|
123
|
+
return getShimPath();
|
|
124
|
+
}
|
|
125
|
+
return undefined;
|
|
126
|
+
},
|
|
127
|
+
load(id) {
|
|
128
|
+
if (id === RESOLVED_IMAGE_LOADER_ID) {
|
|
129
|
+
if (!isEnabled) {
|
|
130
|
+
return `export const defaultLoader = undefined;\nexport const hasCustomLoader = false;\nexport default undefined;\n`;
|
|
131
|
+
}
|
|
132
|
+
const detected = detectImageLoader(options);
|
|
133
|
+
return generateLoaderVirtualModule(detected);
|
|
134
|
+
}
|
|
135
|
+
return undefined;
|
|
136
|
+
},
|
|
137
|
+
transform(code, id) {
|
|
138
|
+
if (!isEnabled)
|
|
139
|
+
return undefined;
|
|
140
|
+
if (id.includes("node_modules"))
|
|
141
|
+
return undefined;
|
|
142
|
+
if (id === getShimPath())
|
|
143
|
+
return undefined;
|
|
144
|
+
if (!/from\s*["']next\/image["']/.test(code))
|
|
145
|
+
return undefined;
|
|
146
|
+
const next = code.replace(/(import\s+(?!type\s)[^;]*?from\s*)(["'])next\/image\2/g, `$1$2${VIRTUAL_IMAGE_SHIM_ID}$2`);
|
|
147
|
+
return next === code ? undefined : { code: next, map: null };
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
export default autoImageLoaderPlugin;
|
package/dist/src/vite/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { autoClientMessagesPlugin, type AutoClientMessagesOptions } from "./auto_client_messages_plugin.js";
|
|
1
2
|
export { autoDynamicPagesPlugin, type AutoDynamicPagesPluginOptions } from "./auto_dynamic_pages_plugin.js";
|
|
2
3
|
export { autoLocaleParamsPlugin, type AutoLocaleParamsPluginOptions } from "./auto_locale_params_plugin.js";
|
|
3
4
|
export { layoutQueriesPlugin, type LayoutQueriesPluginOptions } from "./layout_queries_plugin.js";
|
|
@@ -11,5 +12,6 @@ export { reactEvalStubPlugin, reactEvalEsbuildPlugin, transformReactEval, EVAL_W
|
|
|
11
12
|
export { vinextRouteWiringFixPlugin, patchAppPageRouteWiring, isAppPageRouteWiringFile, isAppPageRouteWiringAlreadyFixed, isVinextAppPageRouteWiringSafeOnDisk, patchAppPageProbe, isAppPageProbeFile, isAppPageProbeAlreadyFixed, isVinextAppPageProbeSafeOnDisk, type VinextRouteWiringFixPluginOptions, } from "./vinext_route_wiring_fix.js";
|
|
12
13
|
export { localeFilePlugin, resolveDefaultIntlConfigPath, type LocaleFilePluginOptions } from "./locale_file_plugin.js";
|
|
13
14
|
export { lucideOptimizerPlugin, detectLucideReact, resolveLucideEsmEntry, parseLucideIconMap, transformLucideImports, transformNextJsImports, type LucideOptimizerPluginOptions, } from "./lucide_optimizer_plugin.js";
|
|
15
|
+
export { autoImageLoaderPlugin, detectImageLoader, findExportedLoaderName, generateLoaderVirtualModule, DEFAULT_LOADER_CANDIDATES, VIRTUAL_IMAGE_LOADER_ID, RESOLVED_IMAGE_LOADER_ID, type AutoImageLoaderOptions, type DetectedImageLoader, } from "./auto_image_loader_plugin.js";
|
|
14
16
|
export { cloudflareNextIntl, cloudflareNextIntlPlugin, type CloudflareNextIntlOptions, default } from "./plugin.js";
|
|
15
17
|
export { imageOptimizer, imageOptimizerPlugin, VIRTUAL_IMAGE_SHIM_ID, type ImageFormat, type ImageBlurOptions, type ImageOverrideOptions, type ImageOptimizerPluginOptions, type ResolvedBlurOptions, type ResolvedOptions, type ResolvedImageConfig, type OptimizedImage, type ManifestData, type ManifestEntry, } from "../image_optimizer/index.js";
|
package/dist/src/vite/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { autoClientMessagesPlugin } from "./auto_client_messages_plugin.js";
|
|
1
2
|
export { autoDynamicPagesPlugin } from "./auto_dynamic_pages_plugin.js";
|
|
2
3
|
export { autoLocaleParamsPlugin } from "./auto_locale_params_plugin.js";
|
|
3
4
|
export { layoutQueriesPlugin } from "./layout_queries_plugin.js";
|
|
@@ -11,5 +12,6 @@ export { reactEvalStubPlugin, reactEvalEsbuildPlugin, transformReactEval, EVAL_W
|
|
|
11
12
|
export { vinextRouteWiringFixPlugin, patchAppPageRouteWiring, isAppPageRouteWiringFile, isAppPageRouteWiringAlreadyFixed, isVinextAppPageRouteWiringSafeOnDisk, patchAppPageProbe, isAppPageProbeFile, isAppPageProbeAlreadyFixed, isVinextAppPageProbeSafeOnDisk, } from "./vinext_route_wiring_fix.js";
|
|
12
13
|
export { localeFilePlugin, resolveDefaultIntlConfigPath } from "./locale_file_plugin.js";
|
|
13
14
|
export { lucideOptimizerPlugin, detectLucideReact, resolveLucideEsmEntry, parseLucideIconMap, transformLucideImports, transformNextJsImports, } from "./lucide_optimizer_plugin.js";
|
|
15
|
+
export { autoImageLoaderPlugin, detectImageLoader, findExportedLoaderName, generateLoaderVirtualModule, DEFAULT_LOADER_CANDIDATES, VIRTUAL_IMAGE_LOADER_ID, RESOLVED_IMAGE_LOADER_ID, } from "./auto_image_loader_plugin.js";
|
|
14
16
|
export { cloudflareNextIntl, cloudflareNextIntlPlugin, default } from "./plugin.js";
|
|
15
17
|
export { imageOptimizer, imageOptimizerPlugin, VIRTUAL_IMAGE_SHIM_ID, } from "../image_optimizer/index.js";
|
|
@@ -3,6 +3,8 @@ import { type LocaleFilePluginOptions } from "./locale_file_plugin.js";
|
|
|
3
3
|
import { type ImageOptimizerPluginOptions } from "../image_optimizer/index.js";
|
|
4
4
|
import { type AutoDynamicPagesPluginOptions } from "./auto_dynamic_pages_plugin.js";
|
|
5
5
|
import { type AutoLocaleParamsPluginOptions } from "./auto_locale_params_plugin.js";
|
|
6
|
+
import { type AutoImageLoaderOptions } from "./auto_image_loader_plugin.js";
|
|
7
|
+
import { type AutoClientMessagesOptions } from "./auto_client_messages_plugin.js";
|
|
6
8
|
import { type LayoutQueriesPluginOptions } from "./layout_queries_plugin.js";
|
|
7
9
|
import { type FirebaseAuthCheckPluginOptions } from "./firebase_auth_check_plugin.js";
|
|
8
10
|
import { type VinextRouteWiringFixPluginOptions } from "./vinext_route_wiring_fix.js";
|
|
@@ -17,6 +19,8 @@ export interface CloudflareNextIntlOptions extends LocaleFilePluginOptions {
|
|
|
17
19
|
bufferStub?: boolean;
|
|
18
20
|
reactEvalStub?: boolean;
|
|
19
21
|
optionalFirebaseStub?: boolean;
|
|
22
|
+
autoImageLoader?: boolean | AutoImageLoaderOptions;
|
|
23
|
+
autoClientMessages?: boolean | AutoClientMessagesOptions;
|
|
20
24
|
imageOptimizer?: boolean | ImageOptimizerPluginOptions;
|
|
21
25
|
vinextRouteWiringFix?: boolean | VinextRouteWiringFixPluginOptions;
|
|
22
26
|
autoLocaleParams?: boolean | AutoLocaleParamsPluginOptions;
|
package/dist/src/vite/plugin.js
CHANGED
|
@@ -8,6 +8,8 @@ import { localeFilePlugin } from "./locale_file_plugin.js";
|
|
|
8
8
|
import { imageOptimizerPlugin } from "../image_optimizer/index.js";
|
|
9
9
|
import { autoDynamicPagesPlugin } from "./auto_dynamic_pages_plugin.js";
|
|
10
10
|
import { autoLocaleParamsPlugin } from "./auto_locale_params_plugin.js";
|
|
11
|
+
import { autoImageLoaderPlugin } from "./auto_image_loader_plugin.js";
|
|
12
|
+
import { autoClientMessagesPlugin } from "./auto_client_messages_plugin.js";
|
|
11
13
|
import { layoutQueriesPlugin } from "./layout_queries_plugin.js";
|
|
12
14
|
import { firebaseAuthCheckPlugin } from "./firebase_auth_check_plugin.js";
|
|
13
15
|
import { vinextRouteWiringFixPlugin } from "./vinext_route_wiring_fix.js";
|
|
@@ -48,6 +50,16 @@ export function cloudflareNextIntl(options = {}) {
|
|
|
48
50
|
}
|
|
49
51
|
plugins.push(autoDynamicPagesPlugin(autoDynamicPagesOptions));
|
|
50
52
|
}
|
|
53
|
+
if (options.autoImageLoader !== false) {
|
|
54
|
+
plugins.push(autoImageLoaderPlugin(typeof options.autoImageLoader === "object"
|
|
55
|
+
? { root: options.root, ...options.autoImageLoader }
|
|
56
|
+
: { root: options.root }));
|
|
57
|
+
}
|
|
58
|
+
if (options.autoClientMessages !== false) {
|
|
59
|
+
plugins.push(autoClientMessagesPlugin(typeof options.autoClientMessages === "object"
|
|
60
|
+
? { root: options.root, ...options.autoClientMessages }
|
|
61
|
+
: { root: options.root }));
|
|
62
|
+
}
|
|
51
63
|
if (options.imageOptimizer !== false) {
|
|
52
64
|
plugins.push(imageOptimizerPlugin(typeof options.imageOptimizer === "object"
|
|
53
65
|
? options.imageOptimizer
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cloudflare-next-intl",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.29",
|
|
4
4
|
"description": "Optimized Next Intl Package Special for App Router and Cloudflare",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -180,6 +180,10 @@
|
|
|
180
180
|
"types": "./dist/src/cookie_consent/client/components/cookie_consent_analytics.d.ts",
|
|
181
181
|
"import": "./dist/src/cookie_consent/client/components/cookie_consent_analytics.js"
|
|
182
182
|
},
|
|
183
|
+
"./AutoAnalyticsEvents": {
|
|
184
|
+
"types": "./dist/src/cookie_consent/client/components/auto_analytics_events.d.ts",
|
|
185
|
+
"import": "./dist/src/cookie_consent/client/components/auto_analytics_events.js"
|
|
186
|
+
},
|
|
183
187
|
"./errorHandling": {
|
|
184
188
|
"types": "./dist/src/error_handling/index.d.ts",
|
|
185
189
|
"import": "./dist/src/error_handling/index.js"
|