Files
Arian Tron 61f56f997c
Some checks failed
Test examples / Test Examples (20) (push) Has been cancelled
Test examples / Test Examples (22) (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Trigger Release / start (push) Has been cancelled
Stale issue handler / stale (push) Has been cancelled
Update Font Data / create-pull-request (push) Has been cancelled
build-and-deploy / deploy-target (push) Has been cancelled
build-and-deploy / build (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / build-wasm (nodejs) (push) Has been cancelled
build-and-deploy / build-wasm (web) (push) Has been cancelled
build-and-deploy / Deploy preview tarball (push) Has been cancelled
build-and-deploy / Potentially publish release (push) Has been cancelled
build-and-deploy / publish-turbopack-npm-packages (push) Has been cancelled
build-and-deploy / Deploy examples (push) Has been cancelled
build-and-deploy / thank you, build (push) Has been cancelled
build-and-deploy / Upload Turbopack Bytesize metrics to Datadog (push) Has been cancelled
Rspack Next.js development integration tests / Rspack integration tests (push) Has been cancelled
Rspack Next.js production integration tests / Rspack integration tests (push) Has been cancelled
Turbopack Next.js development integration tests / Next.js integration tests (push) Has been cancelled
Turbopack Next.js production integration tests / Next.js integration tests (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack development test manifest (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack production test manifest (push) Has been cancelled
Upload bundler test manifests to areweturboyet.com / Upload test results (push) Has been cancelled
Update React / create-pull-request (push) Has been cancelled
test-e2e-project-reset-cron / reset-test-project (push) Has been cancelled
Notify about the top 15 issues/PRs/feature requests (most reacted) in the last 90 days / run (push) Has been cancelled
first commit
2026-03-10 19:37:31 +03:30

143 lines
5.0 KiB
TypeScript

/**
* Describes a file that represents a component definition
*/
export interface ComponentFile {
path: string;
moduleName: string;
componentName: string;
}
export interface PackageDefinition {
name: string;
components: {
moduleName: string;
componentName: string;
}[];
}
const isLazyLoadingModule = (componentPath: string) =>
componentPath.includes(".dynamic");
const removeDynamicModuleNameEnding = (moduleName: string) =>
moduleName.replace(/\.?dynamic$/i, "");
/**
* Generates the contents of the component factory file using a predefined string template.
* @param components - the list of component files to include
* @returns component factory file contents
*/
function generateComponentFactory(
components: (PackageDefinition | ComponentFile)[],
): string {
const componentFiles = components.filter(
(component) => (component as ComponentFile).path,
) as ComponentFile[];
const packages = components.filter(
(component) => (component as PackageDefinition).components,
) as PackageDefinition[];
const hasLazyModules = componentFiles.find((component) =>
isLazyLoadingModule(component.path),
);
return `/* eslint-disable */
// Do not edit this file, it is auto-generated at build time!
// See scripts/generate-component-factory.ts to modify the generation of this file.
${hasLazyModules ? "import dynamic from 'next/dynamic'" : ""}
${packages.map((pkg) => {
const list = pkg.components.map((c) => c.moduleName).join(", ");
return `import { ${list} } from '${pkg.name}'`;
})}
${componentFiles
.map((component) => {
if (isLazyLoadingModule(component.path)) {
const moduleName = removeDynamicModuleNameEnding(component.moduleName);
return `const ${moduleName} = {
module: () => import('${component.path}'),
element: (isEditing?: boolean) => isEditing ? require('${component.path}')?.default : dynamic(${moduleName}.module)
}`;
}
return `import * as ${component.moduleName} from '${component.path}';`;
})
.join("\n")}
const components = new Map();
${packages.map((p) =>
p.components.map(
(component) =>
`components.set('${component.componentName}', ${component.moduleName})`,
),
)}
${componentFiles
.map(
(component) =>
`components.set('${
isLazyLoadingModule(component.path)
? removeDynamicModuleNameEnding(component.componentName)
: component.componentName
}', ${
isLazyLoadingModule(component.path)
? removeDynamicModuleNameEnding(component.moduleName)
: component.moduleName
});`,
)
.join("\n")}
// Next.js 'dynamic' import and JavaScript 'dynamic' import are different.
// Next.js 'dynamic(...)' returns common 'React.ComponentType' while
// 'import('...')' returns 'Promise' that will resolve module.
// componentModule uses 'import(...)' because primary usage of it to get not only 'React Component' (default export) but all named exports.
// See https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports
// componentFactory uses 'dynamic(...)' because primary usage of it to render 'React Component' (default export).
// See https://nextjs.org/docs/advanced-features/dynamic-import
// At the end you will have single preloaded script for each lazy loading module.
// Editing mode doesn't work well with dynamic components in nextjs: dynamic components are not displayed without refresh after a rendering is added.
// This happens because Sitecore editors simply insert updated HTML generated on server side. This conflicts with nextjs dynamic logic as no HTML gets rendered for dynamic component
// So we use require() to obtain dynamic components in editing mode while preserving dynamic logic for non-editing scenarios
// As we need to be able to seamlessly work with dynamic components in both editing and normal modes, different componentFactory functions will be passed to app
export function componentModule(componentName: string) {
const component = components.get(componentName);
// check that component is lazy loading module
if (!component?.default && component?.module) {
// return js dynamic import
return component.module();
}
return component;
}
function baseComponentFactory(componentName: string, exportName?: string, isEditing?: boolean) {
const DEFAULT_EXPORT_NAME = 'Default';
const component = components.get(componentName);
// check that component should be dynamically imported
if (component?.element) {
// return next.js dynamic import
return component.element(isEditing);
}
if (exportName && exportName !== DEFAULT_EXPORT_NAME) {
return component[exportName];
}
return component?.Default || component?.default || component;
}
export function componentFactory(componentName: string, exportName?: string) {
return baseComponentFactory(componentName, exportName, false);
}
export function editingComponentFactory(componentName: string, exportName?: string) {
return baseComponentFactory(componentName, exportName, true);
}
`;
}
export default generateComponentFactory;