libs/sdk/src/lib/journey/config/migrations/classic-journey/pages-json.ts
Properties |
version | |
Type |
string
|
import {
BuildingBlockConfiguration,
LibraryConfiguration,
PluginConfiguration
} from '@allianz/taly-core/schemas';
import chalk from 'chalk';
import { PagesJsonSchema } from '../../../../model';
import { cloneDeepWithJsonComments } from '../shared/utils';
interface PluginConfigWithLibVersion extends PluginConfiguration {
version: string;
}
function isPluginConfigWithLibVersion(
library: PluginConfiguration | PluginConfigWithLibVersion
): library is PluginConfigWithLibVersion {
return (library as PluginConfigWithLibVersion).version !== undefined;
}
export function migratePluginLibrariesToLibraries(pagesConfiguration: PagesJsonSchema) {
if (!pagesConfiguration.plugins || pagesConfiguration.plugins.length === 0) {
return pagesConfiguration;
}
const pluginLibrariesWithVersion: LibraryConfiguration[] = pagesConfiguration.plugins
.filter(isPluginConfigWithLibVersion)
.map((library) => ({
package: removeSecondaryEntryPoint(library.package),
version: library.version
}));
if (pluginLibrariesWithVersion.length === 0) {
return pagesConfiguration;
}
const updatedLibrariesList = pagesConfiguration.libraries.map((library) => {
const libraryVersion =
pluginLibrariesWithVersion.find((lib) => lib.package === library.package)?.version ||
library.version;
return {
package: library.package,
version: libraryVersion
};
});
pluginLibrariesWithVersion.forEach((library) => {
const isLibraryInLibraries = Boolean(
updatedLibrariesList.find((lib) => lib.package === library.package)
);
if (!isLibraryInLibraries) {
updatedLibrariesList.push(library);
}
});
const updatedPagesConfiguration = cloneDeepWithJsonComments(pagesConfiguration);
updatedPagesConfiguration.libraries = updatedLibrariesList;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
updatedPagesConfiguration.plugins = updatedPagesConfiguration.plugins!.map((plugin) => {
if (isPluginConfigWithLibVersion(plugin)) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { version, ...rest } = plugin;
return rest;
}
return plugin;
});
return updatedPagesConfiguration;
}
export function moveStageConfigFromPageDataToTitle(
pagesConfiguration: PagesJsonSchema
): PagesJsonSchema {
const result = cloneDeepWithJsonComments(pagesConfiguration);
result.pages
// pageData.stage does not exist in the new schema
// so we "need" to use `any` assertions here
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.filter((page) => (page.pageData as any)?.stage)
// the types (old vs. new) get in our way here, so we treat the page as "any"
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.forEach((page: any) => {
// ensure the title is an object
if (typeof page.title === 'string') {
page.title = { headline: page.title };
}
page.title ??= {};
const pageData = page.pageData;
// move the stage configuration into the pages title object
const { subHeadline, headline, ...stageConfig } = pageData.stage;
page.title = {
...page.title,
showAsStage: true,
...stageConfig,
...(subHeadline !== undefined
? { topline: migrateSInterpolationToCurlyBraceInterpolation(subHeadline) }
: {}),
...(headline !== undefined
? { headline: migrateSInterpolationToCurlyBraceInterpolation(headline) }
: {
headline:
page.title.headline ??
'⚠️ PLACEHOLDER FOR THE HEADLINE SET BY A TALY MIGRATION. REPLACE ME! ⚠️'
})
};
delete pageData.stage;
if (Object.keys(pageData).length === 0) {
delete page.pageData;
}
});
return result;
}
export function makeStickyBuildingBlockTheBannerBlock(
pagesConfiguration: PagesJsonSchema
): PagesJsonSchema {
const result = cloneDeepWithJsonComments(pagesConfiguration);
result.pages.forEach((page) => {
if (!page.blocks || page.blocks.length === 0) {
return;
}
// why do we ignore errors/warnings in the following lines?
// the new schema does not have a sticky property in the bbStyle, so we cast to any
// general upfront cleanup: remove any `sticky: false` property
const explicitlyNonstickyBlocks = page.blocks.filter(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(block) => ((block as BuildingBlockConfiguration).buildingBlockStyle as any)?.sticky === false
);
explicitlyNonstickyBlocks.forEach((block) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
delete ((block as BuildingBlockConfiguration).buildingBlockStyle as any).sticky;
// also delete the entire buildingBlockStyle if it is empty now
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if (Object.keys((block as BuildingBlockConfiguration).buildingBlockStyle!).length === 0) {
delete (block as BuildingBlockConfiguration).buildingBlockStyle;
}
});
const stickyBlocks = page.blocks.filter(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(block) => ((block as BuildingBlockConfiguration).buildingBlockStyle as any)?.sticky === true
);
if (stickyBlocks.length === 0) {
return;
}
if (stickyBlocks.length > 1) {
console.error(
chalk.red(
`Only one sticky Building Block is allowed per page. Please migrate the page "${chalk.bold(
page.id
)}" manually.`
)
);
return;
}
page.bannerBlock = cloneDeepWithJsonComments(stickyBlocks[0]) as BuildingBlockConfiguration;
// delete the sticky property from the buildingBlockStyle
// eslint-disable-next-line @typescript-eslint/no-explicit-any
delete (page.bannerBlock.buildingBlockStyle as any).sticky;
// also delete the entire buildingBlockStyle if it is empty now
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if (Object.keys(page.bannerBlock.buildingBlockStyle!).length === 0) {
delete page.bannerBlock.buildingBlockStyle;
}
page.blocks = page.blocks.filter((block) => block !== stickyBlocks[0]);
});
return result;
}
/**
* This function will remove anything that looks like a secondary entry point from a package name.
* It works for scoped packages as well as unscoped packages.
*
* | Input | Output |
* | :---- | :----- |
* | `@scope/package` | `@scope/package` |
* | `@scope/package/entry-point` | `@scope/package` |
* | `package` | `package` |
* | `package/entry-point` | `package` |
*
* @param packageName the package name to remove the secondary entry point from
*/
function removeSecondaryEntryPoint(packageName: string): string {
const endIndex = packageName.startsWith('@') ? 2 : 1;
return packageName.split('/').slice(0, endIndex).join('/');
}
function migrateSInterpolationToCurlyBraceInterpolation(source: string): string {
const resourcesMatcher = /s\(["'](.*?)["']\)/g;
const resourcesReplacer = '{$1}';
return source.replace(resourcesMatcher, resourcesReplacer);
}