Skip to content

Commit

Permalink
Merge pull request #25 from ckeditor/ck/24
Browse files Browse the repository at this point in the history
Feature: CKEditorCloudResult now returns more narrowly typed attributes for CKBox and CKEditorPremiumFeatures, taking into account CKEditorCloudConfig. Closes #24
  • Loading branch information
Mati365 authored Sep 9, 2024
2 parents 18fccd2 + cc541e4 commit 30de408
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 11 deletions.
30 changes: 20 additions & 10 deletions src/cdn/loadCKEditorCloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import {
type CKBoxCdnBundlePackConfig
} from './ckbox/createCKBoxCdnBundlePack';

import type { ConditionalBlank } from '../types/ConditionalBlank';
import type { CKCdnVersion } from './ck/createCKCdnUrl';

import {
loadCKCdnResourcesPack,
type InferCKCdnResourcesPackExportsType
Expand Down Expand Up @@ -70,9 +72,9 @@ import {
* Type of plugins can be inferred if `checkPluginLoaded` is not provided: In this case,
* the type of the plugin will be picked from the global window scope.
*/
export function loadCKEditorCloud<Plugins extends CdnPluginsPacks>(
config: CKEditorCloudConfig<Plugins>
): Promise<CKEditorCloudResult<Plugins>> {
export function loadCKEditorCloud<Config extends CKEditorCloudConfig>(
config: Config
): Promise<CKEditorCloudResult<Config>> {
const {
version, languages, plugins,
premium, ckbox
Expand All @@ -98,34 +100,42 @@ export function loadCKEditorCloud<Plugins extends CdnPluginsPacks>(
loadedPlugins: combineCdnPluginsPacks( plugins ?? {} )
} );

return loadCKCdnResourcesPack( pack ) as Promise<CKEditorCloudResult<Plugins>>;
return loadCKCdnResourcesPack( pack ) as Promise<CKEditorCloudResult<Config>>;
}

/**
* The result of the resolved bundles from CKEditor Cloud Services.
*/
export type CKEditorCloudResult<Plugins extends CdnPluginsPacks = any> = {
export type CKEditorCloudResult<Config extends CKEditorCloudConfig> = {

/**
* The base CKEditor bundle exports.
*/
CKEditor: NonNullable<Window['CKEDITOR']>;

/**
* The CKEditor Premium Features bundle exports.
* The CKBox bundle exports.
* It's available only if the `ckbox` configuration is provided.
*/
CKEditorPremiumFeatures?: Window['CKEDITOR_PREMIUM_FEATURES'];
CKBox: ConditionalBlank<
Config['ckbox'],
Window['CKBox']
>;

/**
* The CKBox bundle exports.
* The CKEditor Premium Features bundle exports.
* It's available only if the `premium` configuration is provided.
*/
CKBox?: Window['CKBox'];
CKEditorPremiumFeatures: ConditionalBlank<
Config['premium'],
Window['CKEDITOR_PREMIUM_FEATURES']
>;

/**
* The additional resources exports.
*/
loadedPlugins: InferCKCdnResourcesPackExportsType<
CombinedPluginsPackWithFallbackScope<Plugins>
CombinedPluginsPackWithFallbackScope<NonNullable<Config['plugins']>>
>;
};

Expand Down
13 changes: 13 additions & 0 deletions src/types/ConditionalBlank.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md.
*/

/**
* A type that allows to set a value or leave it blank conditionally.
* It's useful when we provide config, and depending on the config, we want to set a value or leave it blank.
*/
export type ConditionalBlank<Condition, Value> =
Condition extends true | object ? NonNullable<Value> :
Condition extends boolean ? NonNullable<Value> | undefined :
undefined;
47 changes: 46 additions & 1 deletion tests/cdn/loadCKEditorCloud.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
expectTypeOf, beforeEach, afterEach
} from 'vitest';

import type { AIAdapter } from 'ckeditor5-premium-features';

import { loadCKEditorCloud } from '@/cdn/loadCKEditorCloud';
import { createCKBoxBundlePack } from '@/cdn/ckbox/createCKBoxCdnBundlePack';
import { removeCkCdnLinks, removeCkCdnScripts } from 'tests/_utils/ckCdnMocks';
Expand Down Expand Up @@ -76,7 +78,7 @@ describe( 'loadCKEditorCloud', () => {
expect( loadedPlugins?.Plugin ).toBeDefined();
} );

describe( 'plugins', () => {
describe( 'typings', () => {
it( 'should properly infer type of global variable if checkPluginLoaded is not provided', async () => {
const { loadedPlugins } = await loadCKEditorCloud( {
version: '43.0.0',
Expand All @@ -101,6 +103,49 @@ describe( 'loadCKEditorCloud', () => {

expectTypeOf( loadedPlugins.FakePlugin ).toEqualTypeOf<FakePlugin2>();
} );

it( 'should set CKBox result type as non-nullable if ckbox config is provided', async () => {
const { CKBox } = await loadCKEditorCloud( {
version: '43.0.0',
ckbox: {
version: '2.5.1'
}
} );

expectTypeOf( CKBox ).not.toBeNullable();
} );

it( 'should set CKBox result type as nullable if ckbox config is not provided', async () => {
const { CKBox } = await loadCKEditorCloud( {
version: '43.0.0'
} );

expectTypeOf( CKBox ).toBeUndefined();
} );

it( 'should set CKEditorPremiumFeatures type as non-nullable if premium=true config is provided', async () => {
const { CKEditorPremiumFeatures } = await loadCKEditorCloud( {
version: '43.0.0',
premium: true,
ckbox: {
version: '2.5.1'
}
} );

expectTypeOf( CKEditorPremiumFeatures ).not.toBeNullable();
expectTypeOf( CKEditorPremiumFeatures.AIAdapter ).toEqualTypeOf<typeof AIAdapter>();
} );

it( 'should set CKEditorPremiumFeatures type as nullable if premium=true config is not provided', async () => {
const { CKEditorPremiumFeatures } = await loadCKEditorCloud( {
version: '43.0.0',
ckbox: {
version: '2.5.1'
}
} );

expectTypeOf( CKEditorPremiumFeatures ).toBeUndefined();
} );
} );
} );

Expand Down

0 comments on commit 30de408

Please sign in to comment.