Skip to content
This repository was archived by the owner on Jan 28, 2025. It is now read-only.

Commit 9b64587

Browse files
committed
feat(lambda-at-edge, nextjs-commponent): rewrite from root using accept-language header, other minor bugfixes for locales
1 parent b4666d3 commit 9b64587

19 files changed

+1149
-219
lines changed

packages/libs/lambda-at-edge/src/build.ts

Lines changed: 203 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -614,119 +614,175 @@ class Builder {
614614
}
615615
);
616616

617-
// Copy routes for all specified locales
617+
// Duplicate routes for all specified locales. This is easy matching locale-prefixed routes in handler
618618
if (routesManifest.i18n) {
619-
const defaultLocale = routesManifest.i18n.defaultLocale;
620-
for (const locale of routesManifest.i18n.locales) {
621-
if (locale !== defaultLocale) {
622-
const localeSsrPages: {
623-
nonDynamic: {
624-
[key: string]: string;
625-
};
626-
dynamic: DynamicPageKeyValue;
627-
} = {
628-
nonDynamic: {},
629-
dynamic: {}
630-
};
619+
const localeHtmlPages: {
620+
dynamic: DynamicPageKeyValue;
621+
nonDynamic: {
622+
[key: string]: string;
623+
};
624+
} = {
625+
dynamic: {},
626+
nonDynamic: {}
627+
};
631628

632-
for (const key in ssrPages.nonDynamic) {
633-
const newKey = key === "/" ? `/${locale}` : `/${locale}${key}`;
634-
localeSsrPages.nonDynamic[newKey] = ssrPages.nonDynamic[key];
635-
}
629+
const localeSsgPages: {
630+
dynamic: {
631+
[key: string]: DynamicSsgRoute;
632+
};
633+
nonDynamic: {
634+
[key: string]: SsgRoute;
635+
};
636+
} = {
637+
dynamic: {},
638+
nonDynamic: {}
639+
};
636640

637-
ssrPages.nonDynamic = {
638-
...ssrPages.nonDynamic,
639-
...localeSsrPages.nonDynamic
640-
};
641+
const localeSsrPages: {
642+
nonDynamic: {
643+
[key: string]: string;
644+
};
645+
dynamic: DynamicPageKeyValue;
646+
} = {
647+
nonDynamic: {},
648+
dynamic: {}
649+
};
641650

642-
for (const key in ssrPages.dynamic) {
643-
const newKey = key === "/" ? `/${locale}` : `/${locale}${key}`;
651+
for (const locale of routesManifest.i18n.locales) {
652+
htmlPagesNonDynamicLoop: for (const key in htmlPages.nonDynamic) {
653+
// Locale-prefixed pages don't need to be duplicated
654+
for (const locale of routesManifest.i18n.locales) {
655+
if (key.startsWith(`/${locale}/`)) {
656+
break htmlPagesNonDynamicLoop;
657+
}
658+
}
644659

645-
// Initial default value
646-
localeSsrPages.dynamic[newKey] = { file: "", regex: "" };
647-
const newDynamicSsr = Object.assign(
648-
localeSsrPages.dynamic[newKey],
649-
ssrPages.dynamic[key]
650-
);
660+
const newKey = key === "/" ? `/${locale}` : `/${locale}${key}`;
661+
localeHtmlPages.nonDynamic[newKey] = htmlPages.nonDynamic[
662+
key
663+
].replace("pages/", `pages/${locale}/`);
664+
}
651665

652-
// Need to update the regex
653-
newDynamicSsr.regex = pathToRegexStr(newKey);
654-
}
666+
for (const key in htmlPages.dynamic) {
667+
const newKey = key === "/" ? `/${locale}` : `/${locale}${key}`;
655668

656-
ssrPages.dynamic = {
657-
...ssrPages.dynamic,
658-
...localeSsrPages.dynamic
659-
};
669+
// Initial default value
670+
localeHtmlPages.dynamic[newKey] = { file: "", regex: "" };
671+
const newDynamicHtml = Object.assign(
672+
localeHtmlPages.dynamic[newKey],
673+
htmlPages.dynamic[key]
674+
);
660675

661-
const localeSsgPages: {
662-
dynamic: {
663-
[key: string]: DynamicSsgRoute;
664-
};
665-
nonDynamic: {
666-
[key: string]: SsgRoute;
667-
};
668-
} = {
669-
dynamic: {},
670-
nonDynamic: {}
671-
};
676+
// Need to update the file and regex
677+
newDynamicHtml.file = newDynamicHtml.file.replace(
678+
"pages/",
679+
`pages/${locale}/`
680+
);
681+
newDynamicHtml.regex = pathToRegexStr(newKey);
682+
}
672683

673-
for (const key in ssgPages.nonDynamic) {
674-
const newKey = key === "/" ? `/${locale}` : `/${locale}${key}`;
684+
for (const key in ssrPages.nonDynamic) {
685+
const newKey = key === "/" ? `/${locale}` : `/${locale}${key}`;
686+
localeSsrPages.nonDynamic[newKey] = ssrPages.nonDynamic[key];
687+
}
675688

676-
// Initial default value
677-
localeSsgPages.nonDynamic[newKey] = {
678-
initialRevalidateSeconds: false,
679-
srcRoute: null,
680-
dataRoute: ""
681-
};
689+
for (const key in ssrPages.dynamic) {
690+
const newKey = key === "/" ? `/${locale}` : `/${locale}${key}`;
682691

683-
const newSsgRoute = Object.assign(
684-
localeSsgPages.nonDynamic[newKey],
685-
ssgPages.nonDynamic[key]
686-
);
692+
// Initial default value
693+
localeSsrPages.dynamic[newKey] = { file: "", regex: "" };
694+
const newDynamicSsr = Object.assign(
695+
localeSsrPages.dynamic[newKey],
696+
ssrPages.dynamic[key]
697+
);
687698

688-
// Replace with localized value
689-
newSsgRoute.dataRoute = newSsgRoute.dataRoute.replace(
690-
`/_next/data/${buildId}/`,
691-
`/_next/data/${buildId}/${locale}/`
692-
);
693-
}
699+
// Need to update the regex
700+
newDynamicSsr.regex = pathToRegexStr(newKey);
701+
}
702+
703+
for (const key in ssgPages.nonDynamic) {
704+
const newKey = key === "/" ? `/${locale}` : `/${locale}${key}`;
694705

695-
ssgPages.nonDynamic = {
696-
...ssgPages.nonDynamic,
697-
...localeSsgPages.nonDynamic
706+
// Initial default value
707+
localeSsgPages.nonDynamic[newKey] = {
708+
initialRevalidateSeconds: false,
709+
srcRoute: null,
710+
dataRoute: ""
698711
};
699712

700-
for (const key in ssgPages.dynamic) {
701-
const newKey = key === "/" ? `/${locale}` : `/${locale}${key}`;
702-
localeSsgPages.dynamic[newKey] = ssgPages.dynamic[key];
713+
const newSsgRoute = Object.assign(
714+
localeSsgPages.nonDynamic[newKey],
715+
ssgPages.nonDynamic[key]
716+
);
703717

704-
const newDynamicSsgRoute = localeSsgPages.dynamic[newKey];
718+
// Replace with localized value
719+
newSsgRoute.dataRoute = newSsgRoute.dataRoute.replace(
720+
`/_next/data/${buildId}/`,
721+
`/_next/data/${buildId}/${locale}/`
722+
);
705723

706-
// Replace with localized values
707-
newDynamicSsgRoute.dataRoute = newDynamicSsgRoute.dataRoute.replace(
708-
`/_next/data/${buildId}/`,
709-
`/_next/data/${buildId}/${locale}/`
710-
);
711-
newDynamicSsgRoute.dataRouteRegex = newDynamicSsgRoute.dataRouteRegex.replace(
712-
`/_next/data/${buildId}/`,
713-
`/_next/data/${buildId}/${locale}/`
714-
);
715-
newDynamicSsgRoute.fallback =
716-
typeof newDynamicSsgRoute.fallback === "string"
717-
? newDynamicSsgRoute.fallback.replace("/", `/${locale}/`)
718-
: newDynamicSsgRoute.fallback;
719-
newDynamicSsgRoute.routeRegex = localeSsgPages.dynamic[
720-
newKey
721-
].routeRegex.replace("^/", `^/${locale}/`);
722-
}
724+
newSsgRoute.srcRoute = newSsgRoute.srcRoute
725+
? `/${locale}/${newSsgRoute.srcRoute}`
726+
: newSsgRoute.srcRoute;
727+
}
723728

724-
ssgPages.dynamic = {
725-
...ssgPages.dynamic,
726-
...localeSsgPages.dynamic
727-
};
729+
for (const key in ssgPages.dynamic) {
730+
const newKey = key === "/" ? `/${locale}` : `/${locale}${key}`;
731+
localeSsgPages.dynamic[newKey] = ssgPages.dynamic[key];
732+
733+
const newDynamicSsgRoute = localeSsgPages.dynamic[newKey];
734+
735+
// Replace with localized values
736+
newDynamicSsgRoute.dataRoute = newDynamicSsgRoute.dataRoute.replace(
737+
`/_next/data/${buildId}/`,
738+
`/_next/data/${buildId}/${locale}/`
739+
);
740+
newDynamicSsgRoute.dataRouteRegex = newDynamicSsgRoute.dataRouteRegex.replace(
741+
`/_next/data/${buildId}/`,
742+
`/_next/data/${buildId}/${locale}/`
743+
);
744+
newDynamicSsgRoute.fallback =
745+
typeof newDynamicSsgRoute.fallback === "string"
746+
? newDynamicSsgRoute.fallback.replace("/", `/${locale}/`)
747+
: newDynamicSsgRoute.fallback;
748+
newDynamicSsgRoute.routeRegex = localeSsgPages.dynamic[
749+
newKey
750+
].routeRegex.replace("^/", `^/${locale}/`);
728751
}
729752
}
753+
754+
defaultBuildManifest.pages.ssr = {
755+
dynamic: {
756+
...ssrPages.dynamic,
757+
...localeSsrPages.dynamic
758+
},
759+
nonDynamic: {
760+
...ssrPages.nonDynamic,
761+
...localeSsrPages.nonDynamic
762+
}
763+
};
764+
765+
defaultBuildManifest.pages.ssg = {
766+
nonDynamic: {
767+
...ssgPages.nonDynamic,
768+
...localeSsgPages.nonDynamic
769+
},
770+
dynamic: {
771+
...ssgPages.dynamic,
772+
...localeSsgPages.dynamic
773+
}
774+
};
775+
776+
defaultBuildManifest.pages.html = {
777+
nonDynamic: {
778+
...htmlPages.nonDynamic,
779+
...localeHtmlPages.nonDynamic
780+
},
781+
dynamic: {
782+
...htmlPages.dynamic,
783+
...localeHtmlPages.dynamic
784+
}
785+
};
730786
}
731787

732788
const publicFiles = await this.readPublicFiles();
@@ -874,16 +930,26 @@ class Builder {
874930
);
875931
const destination = path.join(
876932
assetOutputDirectory,
877-
withBasePath(
878-
`_next/data/${buildId}/${
879-
defaultLocale && defaultLocale === locale
880-
? JSONFileName
881-
: localePrefixedJSONFileName
882-
}`
883-
)
933+
withBasePath(`_next/data/${buildId}/${localePrefixedJSONFileName}`)
884934
);
885935

886-
return copyIfExists(source, destination);
936+
if (defaultLocale && defaultLocale === locale) {
937+
// If this is default locale, we need to copy to two destinations
938+
// the locale-prefixed path and non-locale-prefixed path
939+
const defaultDestination = path.join(
940+
assetOutputDirectory,
941+
withBasePath(`_next/data/${buildId}/${JSONFileName}`)
942+
);
943+
944+
return new Promise(async () => {
945+
await Promise.all([
946+
copyIfExists(source, destination),
947+
copyIfExists(source, defaultDestination)
948+
]);
949+
});
950+
} else {
951+
return copyIfExists(source, destination);
952+
}
887953
})
888954
);
889955

@@ -902,17 +968,27 @@ class Builder {
902968
const destination = path.join(
903969
assetOutputDirectory,
904970
withBasePath(
905-
path.join(
906-
"static-pages",
907-
buildId,
908-
defaultLocale && defaultLocale === locale
909-
? pageFilePath
910-
: localePrefixedPageFilePath
911-
)
971+
path.join("static-pages", buildId, localePrefixedPageFilePath)
912972
)
913973
);
914974

915-
return copyIfExists(source, destination);
975+
if (defaultLocale && defaultLocale === locale) {
976+
// If this is default locale, we need to copy to two destinations
977+
// the locale-prefixed path and non-locale-prefixed path
978+
const defaultDestination = path.join(
979+
assetOutputDirectory,
980+
withBasePath(path.join("static-pages", buildId, pageFilePath))
981+
);
982+
983+
return new Promise(async () => {
984+
await Promise.all([
985+
copyIfExists(source, destination),
986+
copyIfExists(source, defaultDestination)
987+
]);
988+
});
989+
} else {
990+
return copyIfExists(source, destination);
991+
}
916992
})
917993
);
918994

@@ -933,17 +1009,27 @@ class Builder {
9331009
const destination = path.join(
9341010
assetOutputDirectory,
9351011
withBasePath(
936-
path.join(
937-
"static-pages",
938-
buildId,
939-
defaultLocale && defaultLocale === locale
940-
? fallback
941-
: localePrefixedFallback
942-
)
1012+
path.join("static-pages", buildId, localePrefixedFallback)
9431013
)
9441014
);
9451015

946-
return copyIfExists(source, destination);
1016+
if (defaultLocale && defaultLocale === locale) {
1017+
// If this is default locale, we need to copy to two destinations
1018+
// the locale-prefixed path and non-locale-prefixed path
1019+
const defaultDestination = path.join(
1020+
assetOutputDirectory,
1021+
withBasePath(path.join("static-pages", buildId, fallback))
1022+
);
1023+
1024+
return new Promise(async () => {
1025+
await Promise.all([
1026+
copyIfExists(source, destination),
1027+
copyIfExists(source, defaultDestination)
1028+
]);
1029+
});
1030+
} else {
1031+
return copyIfExists(source, destination);
1032+
}
9471033
})
9481034
);
9491035
}

0 commit comments

Comments
 (0)