vinext 0.0.33 → 0.0.35
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 +1 -0
- package/dist/build/prerender.d.ts +9 -4
- package/dist/build/prerender.js +27 -9
- package/dist/build/prerender.js.map +1 -1
- package/dist/build/run-prerender.js +4 -1
- package/dist/build/run-prerender.js.map +1 -1
- package/dist/config/next-config.d.ts +7 -1
- package/dist/config/next-config.js +39 -26
- package/dist/config/next-config.js.map +1 -1
- package/dist/deploy.js +52 -4
- package/dist/deploy.js.map +1 -1
- package/dist/entries/app-rsc-entry.js +278 -740
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/pages-server-entry.js +60 -134
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +339 -26
- package/dist/index.js.map +1 -1
- package/dist/plugins/optimize-imports.js +28 -2
- package/dist/plugins/optimize-imports.js.map +1 -1
- package/dist/server/app-browser-entry.js +4 -5
- package/dist/server/app-browser-entry.js.map +1 -1
- package/dist/server/app-page-boundary-render.d.ts +63 -0
- package/dist/server/app-page-boundary-render.js +182 -0
- package/dist/server/app-page-boundary-render.js.map +1 -0
- package/dist/server/app-page-boundary.d.ts +57 -0
- package/dist/server/app-page-boundary.js +60 -0
- package/dist/server/app-page-boundary.js.map +1 -0
- package/dist/server/app-page-execution.d.ts +46 -0
- package/dist/server/app-page-execution.js +109 -0
- package/dist/server/app-page-execution.js.map +1 -0
- package/dist/server/app-page-probe.d.ts +17 -0
- package/dist/server/app-page-probe.js +35 -0
- package/dist/server/app-page-probe.js.map +1 -0
- package/dist/server/app-page-render.d.ts +59 -0
- package/dist/server/app-page-render.js +176 -0
- package/dist/server/app-page-render.js.map +1 -0
- package/dist/server/app-page-request.d.ts +58 -0
- package/dist/server/app-page-request.js +79 -0
- package/dist/server/app-page-request.js.map +1 -0
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-response.js.map +1 -1
- package/dist/server/app-page-stream.d.ts +63 -0
- package/dist/server/app-page-stream.js +98 -0
- package/dist/server/app-page-stream.js.map +1 -0
- package/dist/server/app-ssr-stream.js +6 -4
- package/dist/server/app-ssr-stream.js.map +1 -1
- package/dist/server/pages-page-response.d.ts +54 -0
- package/dist/server/pages-page-response.js +140 -0
- package/dist/server/pages-page-response.js.map +1 -0
- package/dist/server/prod-server.d.ts +13 -1
- package/dist/server/prod-server.js +116 -19
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/seed-cache.d.ts +44 -0
- package/dist/server/seed-cache.js +127 -0
- package/dist/server/seed-cache.js.map +1 -0
- package/dist/server/worker-utils.d.ts +0 -6
- package/dist/server/worker-utils.js +41 -5
- package/dist/server/worker-utils.js.map +1 -1
- package/dist/shims/error-boundary.js +1 -1
- package/dist/shims/font-google-base.js +1 -1
- package/dist/shims/font-google-base.js.map +1 -1
- package/dist/shims/font-google.d.ts +2 -3
- package/dist/shims/font-google.js +2 -3
- package/dist/shims/image.js +4 -2
- package/dist/shims/image.js.map +1 -1
- package/package.json +1 -1
- package/dist/shims/font-google.generated.d.ts +0 -1929
- package/dist/shims/font-google.generated.js +0 -1929
- package/dist/shims/font-google.generated.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ import { generateRscEntry } from "./entries/app-rsc-entry.js";
|
|
|
20
20
|
import { generateSsrEntry } from "./entries/app-ssr-entry.js";
|
|
21
21
|
import { generateBrowserEntry } from "./entries/app-browser-entry.js";
|
|
22
22
|
import { PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_BUILD } from "./shims/constants.js";
|
|
23
|
-
import { loadNextConfig, resolveNextConfig } from "./config/next-config.js";
|
|
23
|
+
import { findNextConfigPath, loadNextConfig, resolveNextConfig, resolveNextConfigInput } from "./config/next-config.js";
|
|
24
24
|
import { scanMetadataFiles } from "./server/metadata-routes.js";
|
|
25
25
|
import { manifestFileWithBase, manifestFilesWithBase, normalizeManifestFile } from "./utils/manifest-paths.js";
|
|
26
26
|
import { asyncHooksStubPlugin } from "./plugins/async-hooks-stub.js";
|
|
@@ -163,6 +163,92 @@ function extractStaticValue(node) {
|
|
|
163
163
|
default: return;
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
|
+
function isRecord(value) {
|
|
167
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
168
|
+
}
|
|
169
|
+
const TSCONFIG_FILES = ["tsconfig.json", "jsconfig.json"];
|
|
170
|
+
function resolveTsconfigPathCandidate(candidate) {
|
|
171
|
+
const candidates = candidate.endsWith(".json") ? [candidate] : [
|
|
172
|
+
candidate,
|
|
173
|
+
`${candidate}.json`,
|
|
174
|
+
path.join(candidate, "tsconfig.json")
|
|
175
|
+
];
|
|
176
|
+
for (const item of candidates) if (fs.existsSync(item) && fs.statSync(item).isFile()) return item;
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
function resolveTsconfigExtends(configPath, specifier) {
|
|
180
|
+
const fromDir = path.dirname(configPath);
|
|
181
|
+
if (specifier.startsWith(".") || specifier.startsWith("/") || specifier.startsWith("\\")) return resolveTsconfigPathCandidate(path.resolve(fromDir, specifier));
|
|
182
|
+
const requireFromConfig = createRequire(configPath);
|
|
183
|
+
const candidates = [
|
|
184
|
+
specifier,
|
|
185
|
+
`${specifier}.json`,
|
|
186
|
+
path.join(specifier, "tsconfig.json")
|
|
187
|
+
];
|
|
188
|
+
for (const item of candidates) try {
|
|
189
|
+
return requireFromConfig.resolve(item);
|
|
190
|
+
} catch {}
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
function materializeTsconfigPathAliases(pathsConfig, baseUrl, projectRoot) {
|
|
194
|
+
const aliases = {};
|
|
195
|
+
for (const [find, rawTargets] of Object.entries(pathsConfig)) {
|
|
196
|
+
const target = Array.isArray(rawTargets) ? rawTargets.find((value) => typeof value === "string") : typeof rawTargets === "string" ? rawTargets : null;
|
|
197
|
+
if (!target) continue;
|
|
198
|
+
if (find.includes("*") || target.includes("*")) {
|
|
199
|
+
if (!find.endsWith("/*") || !target.endsWith("/*")) continue;
|
|
200
|
+
if (find.indexOf("*") !== find.length - 1 || target.indexOf("*") !== target.length - 1) continue;
|
|
201
|
+
const aliasKey = find.slice(0, -2);
|
|
202
|
+
const targetDir = target.slice(0, -2);
|
|
203
|
+
if (!aliasKey || !targetDir) continue;
|
|
204
|
+
aliases[aliasKey] = toViteAliasReplacement(path.resolve(baseUrl, targetDir), projectRoot);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
aliases[find] = toViteAliasReplacement(path.resolve(baseUrl, target), projectRoot);
|
|
208
|
+
}
|
|
209
|
+
return aliases;
|
|
210
|
+
}
|
|
211
|
+
function toViteAliasReplacement(absolutePath, projectRoot) {
|
|
212
|
+
const normalizedPath = absolutePath.replace(/\\/g, "/");
|
|
213
|
+
const rootCandidates = new Set([projectRoot]);
|
|
214
|
+
const realRoot = tryRealpathSync(projectRoot);
|
|
215
|
+
if (realRoot) rootCandidates.add(realRoot);
|
|
216
|
+
const pathCandidates = new Set([absolutePath]);
|
|
217
|
+
const realPath = tryRealpathSync(absolutePath);
|
|
218
|
+
if (realPath) pathCandidates.add(realPath);
|
|
219
|
+
for (const rootCandidate of rootCandidates) for (const pathCandidate of pathCandidates) {
|
|
220
|
+
if (pathCandidate === rootCandidate) return "/";
|
|
221
|
+
const relativeId = relativeWithinRoot(rootCandidate, pathCandidate);
|
|
222
|
+
if (relativeId) return "/" + relativeId;
|
|
223
|
+
}
|
|
224
|
+
return normalizedPath;
|
|
225
|
+
}
|
|
226
|
+
function loadTsconfigPathAliases(configPath, projectRoot, seen = /* @__PURE__ */ new Set()) {
|
|
227
|
+
const normalizedPath = tryRealpathSync(configPath) ?? configPath;
|
|
228
|
+
if (seen.has(normalizedPath)) return {};
|
|
229
|
+
seen.add(normalizedPath);
|
|
230
|
+
let parsed = null;
|
|
231
|
+
try {
|
|
232
|
+
parsed = parseStaticObjectLiteral(fs.readFileSync(normalizedPath, "utf-8"));
|
|
233
|
+
} catch {
|
|
234
|
+
return {};
|
|
235
|
+
}
|
|
236
|
+
if (!parsed) return {};
|
|
237
|
+
let aliases = {};
|
|
238
|
+
if (typeof parsed.extends === "string") {
|
|
239
|
+
const extendedPath = resolveTsconfigExtends(normalizedPath, parsed.extends);
|
|
240
|
+
if (extendedPath) aliases = loadTsconfigPathAliases(extendedPath, projectRoot, seen);
|
|
241
|
+
}
|
|
242
|
+
const compilerOptions = isRecord(parsed.compilerOptions) ? parsed.compilerOptions : null;
|
|
243
|
+
const pathsConfig = compilerOptions && isRecord(compilerOptions.paths) ? compilerOptions.paths : null;
|
|
244
|
+
if (!pathsConfig) return aliases;
|
|
245
|
+
const baseUrl = compilerOptions && typeof compilerOptions.baseUrl === "string" ? compilerOptions.baseUrl : ".";
|
|
246
|
+
const resolvedBaseUrl = path.resolve(path.dirname(normalizedPath), baseUrl);
|
|
247
|
+
return {
|
|
248
|
+
...aliases,
|
|
249
|
+
...materializeTsconfigPathAliases(pathsConfig, resolvedBaseUrl, projectRoot)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
166
252
|
/**
|
|
167
253
|
* Detect Vite major version at runtime by resolving from cwd.
|
|
168
254
|
* The plugin may be installed in a workspace root with Vite 7 but used
|
|
@@ -212,6 +298,19 @@ const POSTCSS_CONFIG_FILES = [
|
|
|
212
298
|
* parallel) all await the same in-flight scan rather than each starting their own.
|
|
213
299
|
*/
|
|
214
300
|
const _postcssCache = /* @__PURE__ */ new Map();
|
|
301
|
+
const _tsconfigAliasCache = /* @__PURE__ */ new Map();
|
|
302
|
+
function resolveTsconfigAliases(projectRoot) {
|
|
303
|
+
if (_tsconfigAliasCache.has(projectRoot)) return _tsconfigAliasCache.get(projectRoot);
|
|
304
|
+
let aliases = {};
|
|
305
|
+
for (const name of TSCONFIG_FILES) {
|
|
306
|
+
const candidate = path.join(projectRoot, name);
|
|
307
|
+
if (!fs.existsSync(candidate)) continue;
|
|
308
|
+
aliases = loadTsconfigPathAliases(candidate, projectRoot);
|
|
309
|
+
break;
|
|
310
|
+
}
|
|
311
|
+
_tsconfigAliasCache.set(projectRoot, aliases);
|
|
312
|
+
return aliases;
|
|
313
|
+
}
|
|
215
314
|
/**
|
|
216
315
|
* Resolve PostCSS string plugin names in a project's PostCSS config.
|
|
217
316
|
*
|
|
@@ -279,9 +378,18 @@ const VIRTUAL_APP_SSR_ENTRY = "virtual:vinext-app-ssr-entry";
|
|
|
279
378
|
const RESOLVED_APP_SSR_ENTRY = "\0" + VIRTUAL_APP_SSR_ENTRY;
|
|
280
379
|
const VIRTUAL_APP_BROWSER_ENTRY = "virtual:vinext-app-browser-entry";
|
|
281
380
|
const RESOLVED_APP_BROWSER_ENTRY = "\0" + VIRTUAL_APP_BROWSER_ENTRY;
|
|
381
|
+
const VIRTUAL_GOOGLE_FONTS = "virtual:vinext-google-fonts";
|
|
382
|
+
const RESOLVED_VIRTUAL_GOOGLE_FONTS = "\0" + VIRTUAL_GOOGLE_FONTS;
|
|
282
383
|
/** Image file extensions handled by the vinext:image-imports plugin.
|
|
283
384
|
* Shared between the Rolldown hook filter and the transform handler regex. */
|
|
284
385
|
const IMAGE_EXTS = "png|jpe?g|gif|webp|avif|svg|ico|bmp|tiff?";
|
|
386
|
+
const GOOGLE_FONT_UTILITY_EXPORTS = new Set([
|
|
387
|
+
"buildGoogleFontsUrl",
|
|
388
|
+
"getSSRFontLinks",
|
|
389
|
+
"getSSRFontStyles",
|
|
390
|
+
"getSSRFontPreloads",
|
|
391
|
+
"createFontLoader"
|
|
392
|
+
]);
|
|
285
393
|
/**
|
|
286
394
|
* Extract the npm package name from a module ID (file path).
|
|
287
395
|
* Returns null if not in node_modules.
|
|
@@ -301,6 +409,102 @@ function getPackageName(id) {
|
|
|
301
409
|
}
|
|
302
410
|
/** Absolute path to vinext's shims directory, used by clientManualChunks. */
|
|
303
411
|
const _shimsDir = path.resolve(__dirname, "shims") + "/";
|
|
412
|
+
const _fontGoogleShimPath = resolveShimModulePath(_shimsDir, "font-google");
|
|
413
|
+
function parseGoogleFontNamedSpecifiers(specifiersStr, forceType = false) {
|
|
414
|
+
return specifiersStr.split(",").map((spec) => spec.trim()).filter(Boolean).map((raw) => {
|
|
415
|
+
const isType = forceType || raw.startsWith("type ");
|
|
416
|
+
const asParts = (isType ? raw.replace(/^type\s+/, "") : raw).split(/\s+as\s+/);
|
|
417
|
+
return {
|
|
418
|
+
imported: asParts[0]?.trim() ?? "",
|
|
419
|
+
local: (asParts[1] || asParts[0] || "").trim(),
|
|
420
|
+
isType,
|
|
421
|
+
raw
|
|
422
|
+
};
|
|
423
|
+
}).filter((spec) => spec.imported.length > 0 && spec.local.length > 0);
|
|
424
|
+
}
|
|
425
|
+
function parseGoogleFontImportClause(clause) {
|
|
426
|
+
const trimmed = clause.trim();
|
|
427
|
+
if (trimmed.startsWith("type ")) {
|
|
428
|
+
const braceStart = trimmed.indexOf("{");
|
|
429
|
+
const braceEnd = trimmed.lastIndexOf("}");
|
|
430
|
+
if (braceStart === -1 || braceEnd === -1) return {
|
|
431
|
+
defaultLocal: null,
|
|
432
|
+
namespaceLocal: null,
|
|
433
|
+
named: []
|
|
434
|
+
};
|
|
435
|
+
return {
|
|
436
|
+
defaultLocal: null,
|
|
437
|
+
namespaceLocal: null,
|
|
438
|
+
named: parseGoogleFontNamedSpecifiers(trimmed.slice(braceStart + 1, braceEnd), true)
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
const braceStart = trimmed.indexOf("{");
|
|
442
|
+
const braceEnd = trimmed.lastIndexOf("}");
|
|
443
|
+
if (braceStart !== -1 && braceEnd !== -1) return {
|
|
444
|
+
defaultLocal: trimmed.slice(0, braceStart).trim().replace(/,\s*$/, "").trim() || null,
|
|
445
|
+
namespaceLocal: null,
|
|
446
|
+
named: parseGoogleFontNamedSpecifiers(trimmed.slice(braceStart + 1, braceEnd))
|
|
447
|
+
};
|
|
448
|
+
const commaIndex = trimmed.indexOf(",");
|
|
449
|
+
if (commaIndex !== -1) {
|
|
450
|
+
const defaultLocal = trimmed.slice(0, commaIndex).trim() || null;
|
|
451
|
+
const rest = trimmed.slice(commaIndex + 1).trim();
|
|
452
|
+
if (rest.startsWith("* as ")) return {
|
|
453
|
+
defaultLocal,
|
|
454
|
+
namespaceLocal: rest.slice(5).trim() || null,
|
|
455
|
+
named: []
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
if (trimmed.startsWith("* as ")) return {
|
|
459
|
+
defaultLocal: null,
|
|
460
|
+
namespaceLocal: trimmed.slice(5).trim() || null,
|
|
461
|
+
named: []
|
|
462
|
+
};
|
|
463
|
+
return {
|
|
464
|
+
defaultLocal: trimmed || null,
|
|
465
|
+
namespaceLocal: null,
|
|
466
|
+
named: []
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
function encodeGoogleFontsVirtualId(payload) {
|
|
470
|
+
const params = new URLSearchParams();
|
|
471
|
+
if (payload.hasDefault) params.set("default", "1");
|
|
472
|
+
if (payload.fonts.length > 0) params.set("fonts", payload.fonts.join(","));
|
|
473
|
+
if (payload.utilities.length > 0) params.set("utilities", payload.utilities.join(","));
|
|
474
|
+
return `${VIRTUAL_GOOGLE_FONTS}?${params.toString()}`;
|
|
475
|
+
}
|
|
476
|
+
function parseGoogleFontsVirtualId(id) {
|
|
477
|
+
const cleanId = id.startsWith("\0") ? id.slice(1) : id;
|
|
478
|
+
if (!cleanId.startsWith(VIRTUAL_GOOGLE_FONTS)) return null;
|
|
479
|
+
const queryIndex = cleanId.indexOf("?");
|
|
480
|
+
const params = new URLSearchParams(queryIndex === -1 ? "" : cleanId.slice(queryIndex + 1));
|
|
481
|
+
return {
|
|
482
|
+
hasDefault: params.get("default") === "1",
|
|
483
|
+
fonts: params.get("fonts")?.split(",").map((value) => value.trim()).filter(Boolean) ?? [],
|
|
484
|
+
utilities: params.get("utilities")?.split(",").map((value) => value.trim()).filter(Boolean) ?? []
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
function generateGoogleFontsVirtualModule(id) {
|
|
488
|
+
const payload = parseGoogleFontsVirtualId(id);
|
|
489
|
+
if (!payload) return null;
|
|
490
|
+
const utilities = Array.from(new Set(payload.utilities));
|
|
491
|
+
const fonts = Array.from(new Set(payload.fonts));
|
|
492
|
+
const lines = [];
|
|
493
|
+
lines.push(`import { createFontLoader } from ${JSON.stringify(_fontGoogleShimPath)};`);
|
|
494
|
+
const reExports = [];
|
|
495
|
+
if (payload.hasDefault) reExports.push("default");
|
|
496
|
+
reExports.push(...utilities);
|
|
497
|
+
if (reExports.length > 0) lines.push(`export { ${reExports.join(", ")} } from ${JSON.stringify(_fontGoogleShimPath)};`);
|
|
498
|
+
for (const fontName of fonts) {
|
|
499
|
+
const family = fontName.replace(/_/g, " ");
|
|
500
|
+
lines.push(`export const ${fontName} = /*#__PURE__*/ createFontLoader(${JSON.stringify(family)});`);
|
|
501
|
+
}
|
|
502
|
+
lines.push("");
|
|
503
|
+
return lines.join("\n");
|
|
504
|
+
}
|
|
505
|
+
function propertyNameToGoogleFontFamily(prop) {
|
|
506
|
+
return prop.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2");
|
|
507
|
+
}
|
|
304
508
|
/**
|
|
305
509
|
* manualChunks function for client builds.
|
|
306
510
|
*
|
|
@@ -499,6 +703,7 @@ function vinext(options = {}) {
|
|
|
499
703
|
let middlewarePath = null;
|
|
500
704
|
let instrumentationPath = null;
|
|
501
705
|
let hasCloudflarePlugin = false;
|
|
706
|
+
let warnedInlineNextConfigOverride = false;
|
|
502
707
|
let hasNitroPlugin = false;
|
|
503
708
|
const shimsDir = path.resolve(__dirname, "shims");
|
|
504
709
|
let nextShimMap = {};
|
|
@@ -743,6 +948,7 @@ function vinext(options = {}) {
|
|
|
743
948
|
root = config.root ?? process.cwd();
|
|
744
949
|
const userResolve = config.resolve;
|
|
745
950
|
const shouldEnableNativeTsconfigPaths = viteMajorVersion >= 8 && userResolve?.tsconfigPaths === void 0;
|
|
951
|
+
const tsconfigPathAliases = resolveTsconfigAliases(root);
|
|
746
952
|
const mode = env?.mode ?? "development";
|
|
747
953
|
const dotenvVars = loadEnv(mode, config.envDir ?? root, "");
|
|
748
954
|
for (const [key, value] of Object.entries(dotenvVars)) if (process.env[key] === void 0) process.env[key] = value;
|
|
@@ -767,7 +973,16 @@ function vinext(options = {}) {
|
|
|
767
973
|
hasPagesDir = fs.existsSync(pagesDir);
|
|
768
974
|
hasAppDir = !options.disableAppRouter && fs.existsSync(appDir);
|
|
769
975
|
const phase = env?.command === "build" ? PHASE_PRODUCTION_BUILD : PHASE_DEVELOPMENT_SERVER;
|
|
770
|
-
|
|
976
|
+
let rawConfig;
|
|
977
|
+
if (options.nextConfig) {
|
|
978
|
+
const diskConfigPath = findNextConfigPath(root);
|
|
979
|
+
if (diskConfigPath && !warnedInlineNextConfigOverride) {
|
|
980
|
+
warnedInlineNextConfigOverride = true;
|
|
981
|
+
console.warn(`[vinext] vinext({ nextConfig }) overrides ${path.basename(diskConfigPath)}. Remove one of the config sources to avoid drift.`);
|
|
982
|
+
}
|
|
983
|
+
rawConfig = await resolveNextConfigInput(options.nextConfig, phase);
|
|
984
|
+
} else rawConfig = await loadNextConfig(root, phase);
|
|
985
|
+
nextConfig = await resolveNextConfig(rawConfig, root);
|
|
771
986
|
fileMatcher = createValidFileMatcher(nextConfig.pageExtensions);
|
|
772
987
|
instrumentationPath = findInstrumentationFile(root, fileMatcher);
|
|
773
988
|
middlewarePath = findMiddlewareFile(root, fileMatcher);
|
|
@@ -916,6 +1131,7 @@ function vinext(options = {}) {
|
|
|
916
1131
|
} },
|
|
917
1132
|
resolve: {
|
|
918
1133
|
alias: {
|
|
1134
|
+
...tsconfigPathAliases,
|
|
919
1135
|
...nextConfig.aliases,
|
|
920
1136
|
...nextShimMap
|
|
921
1137
|
},
|
|
@@ -1059,9 +1275,14 @@ function vinext(options = {}) {
|
|
|
1059
1275
|
if (cleanId === VIRTUAL_RSC_ENTRY) return RESOLVED_RSC_ENTRY;
|
|
1060
1276
|
if (cleanId === VIRTUAL_APP_SSR_ENTRY) return RESOLVED_APP_SSR_ENTRY;
|
|
1061
1277
|
if (cleanId === VIRTUAL_APP_BROWSER_ENTRY) return RESOLVED_APP_BROWSER_ENTRY;
|
|
1278
|
+
if (cleanId.startsWith(VIRTUAL_GOOGLE_FONTS + "?")) return RESOLVED_VIRTUAL_GOOGLE_FONTS + cleanId.slice(27);
|
|
1062
1279
|
if (cleanId.endsWith("/" + VIRTUAL_RSC_ENTRY) || cleanId.endsWith("\\" + VIRTUAL_RSC_ENTRY)) return RESOLVED_RSC_ENTRY;
|
|
1063
1280
|
if (cleanId.endsWith("/" + VIRTUAL_APP_SSR_ENTRY) || cleanId.endsWith("\\" + VIRTUAL_APP_SSR_ENTRY)) return RESOLVED_APP_SSR_ENTRY;
|
|
1064
1281
|
if (cleanId.endsWith("/" + VIRTUAL_APP_BROWSER_ENTRY) || cleanId.endsWith("\\" + VIRTUAL_APP_BROWSER_ENTRY)) return RESOLVED_APP_BROWSER_ENTRY;
|
|
1282
|
+
if (cleanId.includes("/" + VIRTUAL_GOOGLE_FONTS + "?") || cleanId.includes("\\" + VIRTUAL_GOOGLE_FONTS + "?")) {
|
|
1283
|
+
const queryIndex = cleanId.indexOf(VIRTUAL_GOOGLE_FONTS + "?");
|
|
1284
|
+
return RESOLVED_VIRTUAL_GOOGLE_FONTS + cleanId.slice(queryIndex + 27);
|
|
1285
|
+
}
|
|
1065
1286
|
}
|
|
1066
1287
|
},
|
|
1067
1288
|
async load(id) {
|
|
@@ -1084,6 +1305,7 @@ function vinext(options = {}) {
|
|
|
1084
1305
|
}
|
|
1085
1306
|
if (id === RESOLVED_APP_SSR_ENTRY && hasAppDir) return generateSsrEntry(hasPagesDir);
|
|
1086
1307
|
if (id === RESOLVED_APP_BROWSER_ENTRY && hasAppDir) return generateBrowserEntry();
|
|
1308
|
+
if (id.startsWith(RESOLVED_VIRTUAL_GOOGLE_FONTS + "?")) return generateGoogleFontsVirtualModule(id);
|
|
1087
1309
|
}
|
|
1088
1310
|
},
|
|
1089
1311
|
asyncHooksStubPlugin,
|
|
@@ -1571,38 +1793,82 @@ function vinext(options = {}) {
|
|
|
1571
1793
|
},
|
|
1572
1794
|
transform: {
|
|
1573
1795
|
filter: {
|
|
1574
|
-
id: {
|
|
1575
|
-
include: /\.(tsx?|jsx?|mjs)$/,
|
|
1576
|
-
exclude: /node_modules/
|
|
1577
|
-
},
|
|
1796
|
+
id: { include: /\.(tsx?|jsx?|mjs)$/ },
|
|
1578
1797
|
code: "next/font/google"
|
|
1579
1798
|
},
|
|
1580
1799
|
async handler(code, id) {
|
|
1581
|
-
if (!this._isBuild) return null;
|
|
1582
|
-
if (id.includes("node_modules")) return null;
|
|
1583
1800
|
if (id.startsWith("\0")) return null;
|
|
1584
1801
|
if (!id.match(/\.(tsx?|jsx?|mjs)$/)) return null;
|
|
1585
1802
|
if (!code.includes("next/font/google")) return null;
|
|
1586
|
-
|
|
1587
|
-
const importMatch = code.match(/import\s*\{([^}]+)\}\s*from\s*['"]next\/font\/google['"]/);
|
|
1588
|
-
if (!importMatch) return null;
|
|
1589
|
-
const importedNames = new Set(importMatch[1].split(",").map((s) => s.trim()).filter(Boolean));
|
|
1803
|
+
if (id.startsWith(_shimsDir)) return null;
|
|
1590
1804
|
const s = new MagicString(code);
|
|
1591
1805
|
let hasChanges = false;
|
|
1806
|
+
let proxyImportCounter = 0;
|
|
1807
|
+
const overwrittenRanges = [];
|
|
1808
|
+
const fontLocals = /* @__PURE__ */ new Map();
|
|
1809
|
+
const proxyObjectLocals = /* @__PURE__ */ new Set();
|
|
1810
|
+
const importRe = /^[ \t]*import\s+([^;]+?)\s+from\s*(["'])next\/font\/google\2\s*;?/gm;
|
|
1811
|
+
let importMatch;
|
|
1812
|
+
while ((importMatch = importRe.exec(code)) !== null) {
|
|
1813
|
+
const [fullMatch, clause] = importMatch;
|
|
1814
|
+
const matchStart = importMatch.index;
|
|
1815
|
+
const matchEnd = matchStart + fullMatch.length;
|
|
1816
|
+
const parsed = parseGoogleFontImportClause(clause);
|
|
1817
|
+
const utilityImports = parsed.named.filter((spec) => !spec.isType && GOOGLE_FONT_UTILITY_EXPORTS.has(spec.imported));
|
|
1818
|
+
const fontImports = parsed.named.filter((spec) => !spec.isType && !GOOGLE_FONT_UTILITY_EXPORTS.has(spec.imported));
|
|
1819
|
+
if (parsed.defaultLocal) proxyObjectLocals.add(parsed.defaultLocal);
|
|
1820
|
+
for (const fontImport of fontImports) fontLocals.set(fontImport.local, fontImport.imported);
|
|
1821
|
+
if (fontImports.length > 0) {
|
|
1822
|
+
const virtualId = encodeGoogleFontsVirtualId({
|
|
1823
|
+
hasDefault: Boolean(parsed.defaultLocal),
|
|
1824
|
+
fonts: Array.from(new Set(fontImports.map((spec) => spec.imported))),
|
|
1825
|
+
utilities: Array.from(new Set(utilityImports.map((spec) => spec.imported)))
|
|
1826
|
+
});
|
|
1827
|
+
s.overwrite(matchStart, matchEnd, `import ${clause} from ${JSON.stringify(virtualId)};`);
|
|
1828
|
+
overwrittenRanges.push([matchStart, matchEnd]);
|
|
1829
|
+
hasChanges = true;
|
|
1830
|
+
continue;
|
|
1831
|
+
}
|
|
1832
|
+
if (parsed.namespaceLocal) {
|
|
1833
|
+
const proxyImportName = `__vinext_google_fonts_proxy_${proxyImportCounter++}`;
|
|
1834
|
+
const replacementLines = [`import ${proxyImportName} from ${JSON.stringify(_fontGoogleShimPath)};`];
|
|
1835
|
+
if (parsed.defaultLocal) replacementLines.push(`var ${parsed.defaultLocal} = ${proxyImportName};`);
|
|
1836
|
+
replacementLines.push(`var ${parsed.namespaceLocal} = ${proxyImportName};`);
|
|
1837
|
+
s.overwrite(matchStart, matchEnd, replacementLines.join("\n"));
|
|
1838
|
+
overwrittenRanges.push([matchStart, matchEnd]);
|
|
1839
|
+
proxyObjectLocals.add(parsed.namespaceLocal);
|
|
1840
|
+
hasChanges = true;
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
const exportRe = /^[ \t]*export\s*\{([^}]+)\}\s*from\s*(["'])next\/font\/google\2\s*;?/gm;
|
|
1844
|
+
let exportMatch;
|
|
1845
|
+
while ((exportMatch = exportRe.exec(code)) !== null) {
|
|
1846
|
+
const [fullMatch, specifiers] = exportMatch;
|
|
1847
|
+
const matchStart = exportMatch.index;
|
|
1848
|
+
const matchEnd = matchStart + fullMatch.length;
|
|
1849
|
+
const namedExports = parseGoogleFontNamedSpecifiers(specifiers);
|
|
1850
|
+
const utilityExports = namedExports.filter((spec) => !spec.isType && GOOGLE_FONT_UTILITY_EXPORTS.has(spec.imported));
|
|
1851
|
+
const fontExports = namedExports.filter((spec) => !spec.isType && !GOOGLE_FONT_UTILITY_EXPORTS.has(spec.imported));
|
|
1852
|
+
if (fontExports.length === 0) continue;
|
|
1853
|
+
const virtualId = encodeGoogleFontsVirtualId({
|
|
1854
|
+
hasDefault: false,
|
|
1855
|
+
fonts: Array.from(new Set(fontExports.map((spec) => spec.imported))),
|
|
1856
|
+
utilities: Array.from(new Set(utilityExports.map((spec) => spec.imported)))
|
|
1857
|
+
});
|
|
1858
|
+
s.overwrite(matchStart, matchEnd, `export { ${specifiers.trim()} } from ${JSON.stringify(virtualId)};`);
|
|
1859
|
+
overwrittenRanges.push([matchStart, matchEnd]);
|
|
1860
|
+
hasChanges = true;
|
|
1861
|
+
}
|
|
1592
1862
|
const cacheDir = this._cacheDir;
|
|
1593
1863
|
const fontCache = this._fontCache;
|
|
1594
|
-
|
|
1595
|
-
while ((match = fontCallRe.exec(code)) !== null) {
|
|
1596
|
-
const [fullMatch, fontName, optionsStr] = match;
|
|
1597
|
-
if (!importedNames.has(fontName)) continue;
|
|
1598
|
-
const family = fontName.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2");
|
|
1864
|
+
async function injectSelfHostedCss(callStart, callEnd, optionsStr, family, calleeSource) {
|
|
1599
1865
|
let options = {};
|
|
1600
1866
|
try {
|
|
1601
1867
|
const parsed = parseStaticObjectLiteral(optionsStr);
|
|
1602
|
-
if (!parsed)
|
|
1868
|
+
if (!parsed) return;
|
|
1603
1869
|
options = parsed;
|
|
1604
1870
|
} catch {
|
|
1605
|
-
|
|
1871
|
+
return;
|
|
1606
1872
|
}
|
|
1607
1873
|
const weights = options.weight ? Array.isArray(options.weight) ? options.weight : [options.weight] : [];
|
|
1608
1874
|
const styles = options.style ? Array.isArray(options.style) ? options.style : [options.style] : [];
|
|
@@ -1626,16 +1892,37 @@ function vinext(options = {}) {
|
|
|
1626
1892
|
localCSS = await fetchAndCacheFont(cssUrl, family, cacheDir);
|
|
1627
1893
|
fontCache.set(cssUrl, localCSS);
|
|
1628
1894
|
} catch {
|
|
1629
|
-
|
|
1895
|
+
return;
|
|
1630
1896
|
}
|
|
1631
|
-
const matchStart = match.index;
|
|
1632
|
-
const matchEnd = matchStart + fullMatch.length;
|
|
1633
1897
|
const escapedCSS = JSON.stringify(localCSS);
|
|
1634
1898
|
const closingBrace = optionsStr.lastIndexOf("}");
|
|
1635
|
-
const replacement = `${
|
|
1636
|
-
s.overwrite(
|
|
1899
|
+
const replacement = `${calleeSource}(${optionsStr.slice(0, closingBrace) + (optionsStr.slice(0, closingBrace).trim().endsWith("{") ? "" : ", ") + `_selfHostedCSS: ${escapedCSS}` + optionsStr.slice(closingBrace)})`;
|
|
1900
|
+
s.overwrite(callStart, callEnd, replacement);
|
|
1637
1901
|
hasChanges = true;
|
|
1638
1902
|
}
|
|
1903
|
+
if (this._isBuild) {
|
|
1904
|
+
const namedCallRe = /\b([A-Za-z_$][A-Za-z0-9_$]*)\s*\(\s*(\{[^}]*\})\s*\)/g;
|
|
1905
|
+
let namedCallMatch;
|
|
1906
|
+
while ((namedCallMatch = namedCallRe.exec(code)) !== null) {
|
|
1907
|
+
const [fullMatch, localName, optionsStr] = namedCallMatch;
|
|
1908
|
+
const importedName = fontLocals.get(localName);
|
|
1909
|
+
if (!importedName) continue;
|
|
1910
|
+
const callStart = namedCallMatch.index;
|
|
1911
|
+
const callEnd = callStart + fullMatch.length;
|
|
1912
|
+
if (overwrittenRanges.some(([start, end]) => callStart < end && callEnd > start)) continue;
|
|
1913
|
+
await injectSelfHostedCss(callStart, callEnd, optionsStr, importedName.replace(/_/g, " "), localName);
|
|
1914
|
+
}
|
|
1915
|
+
const memberCallRe = /\b([A-Za-z_$][A-Za-z0-9_$]*)\.([A-Za-z_$][A-Za-z0-9_$]*)\s*\(\s*(\{[^}]*\})\s*\)/g;
|
|
1916
|
+
let memberCallMatch;
|
|
1917
|
+
while ((memberCallMatch = memberCallRe.exec(code)) !== null) {
|
|
1918
|
+
const [fullMatch, objectName, propName, optionsStr] = memberCallMatch;
|
|
1919
|
+
if (!proxyObjectLocals.has(objectName)) continue;
|
|
1920
|
+
const callStart = memberCallMatch.index;
|
|
1921
|
+
const callEnd = callStart + fullMatch.length;
|
|
1922
|
+
if (overwrittenRanges.some(([start, end]) => callStart < end && callEnd > start)) continue;
|
|
1923
|
+
await injectSelfHostedCss(callStart, callEnd, optionsStr, propertyNameToGoogleFontFamily(propName), `${objectName}.${propName}`);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1639
1926
|
if (!hasChanges) return null;
|
|
1640
1927
|
return {
|
|
1641
1928
|
code: s.toString(),
|
|
@@ -2036,8 +2323,34 @@ function vinext(options = {}) {
|
|
|
2036
2323
|
const yogaWasmPath = path.join(distDir, "yoga.wasm");
|
|
2037
2324
|
if (!fs.existsSync(yogaWasmPath)) fs.writeFileSync(yogaWasmPath, Buffer.from(yogaBase64, "base64"));
|
|
2038
2325
|
result = result.replace(yogaMatch[0], `H = "";`);
|
|
2039
|
-
|
|
2040
|
-
|
|
2326
|
+
const YOGA_CALL = `yoga_wasm_base64_esm_default()`;
|
|
2327
|
+
const YOGA_CALL_PATCHED = [
|
|
2328
|
+
`yoga_wasm_base64_esm_default({ instantiateWasm: function(imports, callback) {`,
|
|
2329
|
+
` __vi_yoga_mod.then(function(mod) {`,
|
|
2330
|
+
` if (mod) {`,
|
|
2331
|
+
` WebAssembly.instantiate(mod, imports).then(function(inst) { callback(inst); });`,
|
|
2332
|
+
` } else {`,
|
|
2333
|
+
` var b = Buffer.from(__vi_yoga_b64, "base64");`,
|
|
2334
|
+
` WebAssembly.instantiate(b, imports).then(function(r) { callback(r.instance); });`,
|
|
2335
|
+
` }`,
|
|
2336
|
+
` });`,
|
|
2337
|
+
` return {};`,
|
|
2338
|
+
`} })`
|
|
2339
|
+
].join("\n");
|
|
2340
|
+
result = result.replace(YOGA_CALL, YOGA_CALL_PATCHED);
|
|
2341
|
+
result = [`var __vi_yoga_b64 = ${JSON.stringify(yogaBase64)};`, `var __vi_yoga_mod = import("./yoga.wasm?module").then(function(m) { return m.default; }).catch(function() { return null; });`].join("\n") + "\n" + result;
|
|
2342
|
+
}
|
|
2343
|
+
const resvgMatch = /import\s+resvg_wasm\s+from\s+["']\.\/resvg\.wasm\?module["']\s*;?/.exec(result);
|
|
2344
|
+
if (resvgMatch) {
|
|
2345
|
+
const resvgLoader = [
|
|
2346
|
+
`var resvg_wasm = import("./resvg.wasm?module").then(function(m) { return m.default; }).catch(function() {`,
|
|
2347
|
+
` return Promise.all([import("node:fs"), import("node:url")]).then(function(mods) {`,
|
|
2348
|
+
` var p = mods[1].fileURLToPath(new URL("./resvg.wasm", import.meta.url));`,
|
|
2349
|
+
` return mods[0].promises.readFile(p).then(function(buf) { return WebAssembly.compile(buf); });`,
|
|
2350
|
+
` });`,
|
|
2351
|
+
`});`
|
|
2352
|
+
].join("\n");
|
|
2353
|
+
result = result.replace(resvgMatch[0], resvgLoader);
|
|
2041
2354
|
}
|
|
2042
2355
|
if (result === code) return null;
|
|
2043
2356
|
return {
|