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
143 lines
5.0 KiB
TypeScript
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;
|