diff --git a/src/Umbraco.Web.UI.Client/.eslintrc.json b/src/Umbraco.Web.UI.Client/.eslintrc.json index d021d2caf6..69579d3900 100644 --- a/src/Umbraco.Web.UI.Client/.eslintrc.json +++ b/src/Umbraco.Web.UI.Client/.eslintrc.json @@ -2,7 +2,6 @@ "ignorePatterns": ["vite.*.ts"], "root": true, "plugins": ["eslint-plugin-local-rules"], - "extends": ["eslint:recommended", "plugin:import/recommended", "prettier"], "overrides": [ { "files": ["**/*.ts"], @@ -49,6 +48,27 @@ } } } + }, + { + "files": ["**/*.js"], + "extends": ["eslint:recommended", "plugin:import/recommended", "prettier"], + "env": { + "node": true, + "browser": true, + "es6": true + }, + "parserOptions": { + "sourceType": "module", + "ecmaVersion": "latest" + }, + "settings": { + "import/resolver": { + "node": { + "extensions": [".js"], + "moduleDirectory": ["node_modules"] + } + } + } } ] } diff --git a/src/Umbraco.Web.UI.Client/.gitignore b/src/Umbraco.Web.UI.Client/.gitignore index 203142b8ae..48fcabe2ce 100644 --- a/src/Umbraco.Web.UI.Client/.gitignore +++ b/src/Umbraco.Web.UI.Client/.gitignore @@ -11,6 +11,7 @@ node_modules dist dist-ssr types +*.tsbuildinfo *.local *.tgz diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/index.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/index.ts index c90638336d..dd1647bd85 100644 --- a/src/Umbraco.Web.UI.Client/libs/backend-api/src/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/index.ts @@ -91,6 +91,7 @@ export type { OutOfDateStatusModel } from './models/OutOfDateStatusModel'; export { OutOfDateTypeModel } from './models/OutOfDateTypeModel'; export type { PackageCreateModel } from './models/PackageCreateModel'; export type { PackageDefinitionModel } from './models/PackageDefinitionModel'; +export type { PackageManifestModel } from './models/PackageManifestModel'; export type { PackageMigrationStatusModel } from './models/PackageMigrationStatusModel'; export type { PackageModelBaseModel } from './models/PackageModelBaseModel'; export type { PackageUpdateModel } from './models/PackageUpdateModel'; diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PackageManifestModel.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PackageManifestModel.ts new file mode 100644 index 0000000000..0c0518f0ed --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/models/PackageManifestModel.ts @@ -0,0 +1,10 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type PackageManifestModel = { + name?: string; + version?: string | null; + extensions?: Array; +}; + diff --git a/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/PackageResource.ts b/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/PackageResource.ts index cc80617938..4af66aa239 100644 --- a/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/PackageResource.ts +++ b/src/Umbraco.Web.UI.Client/libs/backend-api/src/services/PackageResource.ts @@ -3,6 +3,7 @@ /* eslint-disable */ import type { PackageCreateModel } from '../models/PackageCreateModel'; import type { PackageDefinitionModel } from '../models/PackageDefinitionModel'; +import type { PackageManifestModel } from '../models/PackageManifestModel'; import type { PackageUpdateModel } from '../models/PackageUpdateModel'; import type { PagedPackageDefinitionModel } from '../models/PagedPackageDefinitionModel'; import type { PagedPackageMigrationStatusModel } from '../models/PagedPackageMigrationStatusModel'; @@ -166,6 +167,17 @@ export class PackageResource { }); } + /** + * @returns any Success + * @throws ApiError + */ + public static getPackageManifest(): CancelablePromise> { + return __request(OpenAPI, { + method: 'GET', + url: '/umbraco/management/api/v1/package/manifest', + }); + } + /** * @returns PagedPackageMigrationStatusModel Success * @throws ApiError diff --git a/src/Umbraco.Web.UI.Client/libs/element/index.out.ts b/src/Umbraco.Web.UI.Client/libs/element/index.out.ts new file mode 100644 index 0000000000..18d6126ccc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/element/index.out.ts @@ -0,0 +1 @@ +export * from './element.mixin'; diff --git a/src/Umbraco.Web.UI.Client/libs/element/rollup.config.js b/src/Umbraco.Web.UI.Client/libs/element/rollup.config.js index 945c0afe88..e106b525af 100644 --- a/src/Umbraco.Web.UI.Client/libs/element/rollup.config.js +++ b/src/Umbraco.Web.UI.Client/libs/element/rollup.config.js @@ -1,4 +1,5 @@ import config from '../../utils/rollup.config.js'; export default { ...config, + input: 'index.out.ts' }; diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-api/create-extension-element.function.ts b/src/Umbraco.Web.UI.Client/libs/extensions-api/create-extension-element.function.ts index 2cd7f10c0d..f6e1966c0d 100644 --- a/src/Umbraco.Web.UI.Client/libs/extensions-api/create-extension-element.function.ts +++ b/src/Umbraco.Web.UI.Client/libs/extensions-api/create-extension-element.function.ts @@ -22,7 +22,7 @@ export async function createExtensionElement(manifest: ManifestElement): Promise console.error('-- Extension did not succeed creating an element, missing a default export of the served JavaScript file', manifest); - // If some JS was loaded and it did not at least contain a default export, then we are safe to assume that it executed as a module and does not need to be returned + // If some JS was loaded and it did not at least contain a default export, then we are safe to assume that it executed its side effects and does not need to be returned return undefined; } diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-api/has-init-export.function.ts b/src/Umbraco.Web.UI.Client/libs/extensions-api/has-init-export.function.ts new file mode 100644 index 0000000000..a7f957c7e9 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/extensions-api/has-init-export.function.ts @@ -0,0 +1,8 @@ +import type { UmbEntrypointModule } from "./umb-lifecycle.interface"; + +/** + * Validate if an ESModule exports a known init function called 'onInit' + */ +export function hasInitExport(obj: unknown): obj is Pick { + return obj !== null && typeof obj === 'object' && 'onInit' in obj; +} diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-api/index.ts b/src/Umbraco.Web.UI.Client/libs/extensions-api/index.ts index c021ad7224..137291b16a 100644 --- a/src/Umbraco.Web.UI.Client/libs/extensions-api/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/extensions-api/index.ts @@ -3,6 +3,7 @@ import { UmbExtensionRegistry } from './registry/extension.registry'; export * from './registry/extension.registry'; export * from './create-extension-element.function'; export * from './has-default-export.function'; +export * from './has-init-export.function'; export * from './is-manifest-element-name-type.function'; export * from './is-manifest-elementable-type.function'; export * from './is-manifest-js-type.function'; @@ -10,5 +11,6 @@ export * from './is-manifest-loader-type.function'; export * from './load-extension.function'; export * from './create-extension-element-or-fallback.function'; export * from './create-extension-class.function'; +export * from './umb-lifecycle.interface'; export const umbExtensionsRegistry = new UmbExtensionRegistry(); diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-api/load-extension.function.ts b/src/Umbraco.Web.UI.Client/libs/extensions-api/load-extension.function.ts index f1ebdbbe88..516a5d6d68 100644 --- a/src/Umbraco.Web.UI.Client/libs/extensions-api/load-extension.function.ts +++ b/src/Umbraco.Web.UI.Client/libs/extensions-api/load-extension.function.ts @@ -14,10 +14,10 @@ export async function loadExtension(manifest: ManifestElement): Promise = T extends keyof ManifestTypeMap ? ManifestTypeMap[T] @@ -13,7 +15,7 @@ export class UmbExtensionRegistry { private _extensions = new BehaviorSubject>([]); public readonly extensions = this._extensions.asObservable(); - register(manifest: ManifestTypes): void { + register(manifest: ManifestTypes, rootHost?: UmbControllerHostInterface): void { const extensionsValues = this._extensions.getValue(); const extension = extensionsValues.find((extension) => extension.alias === manifest.alias); @@ -27,12 +29,9 @@ export class UmbExtensionRegistry { // If entrypoint extension, we should load and run it immediately if (manifest.type === 'entrypoint') { loadExtension(manifest as ManifestEntrypoint).then((js) => { - if (hasDefaultExport(js)) { - new js.default(); - } else { - console.error( - `Extension with alias '${manifest.alias}' of type 'entrypoint' must have a default export of its JavaScript module.` - ); + // If the extension has an onInit export, be sure to run that or else let the module handle itself + if (hasInitExport(js)) { + js.onInit(rootHost!, this); } }); } @@ -96,3 +95,5 @@ export class UmbExtensionRegistry { ) as Observable>; } } + +export const UMB_EXTENSION_REGISTRY_TOKEN = new UmbContextToken('UmbExtensionRegistry'); diff --git a/src/Umbraco.Web.UI.Client/libs/extensions-api/umb-lifecycle.interface.ts b/src/Umbraco.Web.UI.Client/libs/extensions-api/umb-lifecycle.interface.ts new file mode 100644 index 0000000000..8486453312 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/extensions-api/umb-lifecycle.interface.ts @@ -0,0 +1,9 @@ +import type { UmbControllerHostInterface } from "@umbraco-cms/controller"; +import type { UmbExtensionRegistry } from "./registry/extension.registry"; + +/** + * Interface containing supported life-cycle functions for ESModule entrypoints + */ +export interface UmbEntrypointModule { + onInit: (host: UmbControllerHostInterface, extensionRegistry: UmbExtensionRegistry) => void +} diff --git a/src/Umbraco.Web.UI.Client/libs/models/index.ts b/src/Umbraco.Web.UI.Client/libs/models/index.ts index 93f630e8e2..6a677de13d 100644 --- a/src/Umbraco.Web.UI.Client/libs/models/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/models/index.ts @@ -3,6 +3,7 @@ import { DictionaryItemTranslationModel, EntityTreeItemModel, FolderTreeItemModel, + PackageManifestModel, ProblemDetailsModel, } from '@umbraco-cms/backend-api'; @@ -152,13 +153,9 @@ export interface SwatchDetails { value: string; } -export type UmbPackage = { - name?: string; - version?: string; - extensions?: unknown[]; -}; +export type UmbPackage = PackageManifestModel; -export type PagedManifestsResponse = UmbPackage[]; +export type PackageManifestResponse = UmbPackage[]; export type UmbPackageWithMigrationStatus = UmbPackage & { hasPendingMigrations: boolean; diff --git a/src/Umbraco.Web.UI.Client/libs/notification/index.ts b/src/Umbraco.Web.UI.Client/libs/notification/index.ts index 5ada5c569a..249397c8e5 100644 --- a/src/Umbraco.Web.UI.Client/libs/notification/index.ts +++ b/src/Umbraco.Web.UI.Client/libs/notification/index.ts @@ -1,3 +1,2 @@ export * from './notification.context'; export * from './notification-handler'; -export * from './layouts/default'; diff --git a/src/Umbraco.Web.UI.Client/libs/notification/layouts/default/notification-layout-default.element.ts b/src/Umbraco.Web.UI.Client/libs/notification/layouts/default/notification-layout-default.element.ts index f9beaf21c6..aa54b5be9e 100644 --- a/src/Umbraco.Web.UI.Client/libs/notification/layouts/default/notification-layout-default.element.ts +++ b/src/Umbraco.Web.UI.Client/libs/notification/layouts/default/notification-layout-default.element.ts @@ -1,7 +1,7 @@ import { html, LitElement } from 'lit'; import { customElement, property } from 'lit/decorators.js'; +import { ifDefined } from 'lit/directives/if-defined.js'; import { UUITextStyles } from '@umbraco-ui/uui-css'; -import { ifDefined } from 'lit-html/directives/if-defined.js'; import type { UmbNotificationHandler } from '../..'; export interface UmbNotificationDefaultData { diff --git a/src/Umbraco.Web.UI.Client/libs/notification/notification-handler.ts b/src/Umbraco.Web.UI.Client/libs/notification/notification-handler.ts index 7075151635..c249effe09 100644 --- a/src/Umbraco.Web.UI.Client/libs/notification/notification-handler.ts +++ b/src/Umbraco.Web.UI.Client/libs/notification/notification-handler.ts @@ -1,6 +1,6 @@ import { UUIToastNotificationElement } from '@umbraco-ui/uui'; import { v4 as uuidv4 } from 'uuid'; -import type { UmbNotificationOptions, UmbNotificationData, UmbNotificationColor } from '.'; +import type { UmbNotificationOptions, UmbNotificationColor, UmbNotificationDefaultData } from '.'; import './layouts/default'; @@ -12,7 +12,7 @@ export class UmbNotificationHandler { private _closeResolver: any; private _closePromise: Promise; private _elementName?: string; - private _data: UmbNotificationData; + private _data?: UmbNotificationDefaultData; private _defaultColor: UmbNotificationColor = 'default'; private _defaultDuration = 6000; @@ -28,7 +28,7 @@ export class UmbNotificationHandler { * @param {UmbNotificationOptions} options * @memberof UmbNotificationHandler */ - constructor(options: UmbNotificationOptions) { + constructor(options: UmbNotificationOptions) { this.key = uuidv4(); this.color = options.color || this._defaultColor; this.duration = options.duration !== undefined ? options.duration : this._defaultDuration; diff --git a/src/Umbraco.Web.UI.Client/libs/notification/notification.context.ts b/src/Umbraco.Web.UI.Client/libs/notification/notification.context.ts index 785aa93ad4..d48b21678a 100644 --- a/src/Umbraco.Web.UI.Client/libs/notification/notification.context.ts +++ b/src/Umbraco.Web.UI.Client/libs/notification/notification.context.ts @@ -2,14 +2,22 @@ import { BehaviorSubject } from 'rxjs'; import { UmbNotificationHandler } from './notification-handler'; import { UmbContextToken } from '@umbraco-cms/context-api'; -export type UmbNotificationData = any; +/** + * The default data of notifications + * @export + * @interface UmbNotificationDefaultData + */ +export interface UmbNotificationDefaultData { + message: string; + headline?: string; +} /** * @export * @interface UmbNotificationOptions * @template UmbNotificationData */ -export interface UmbNotificationOptions { +export interface UmbNotificationOptions { color?: UmbNotificationColor; duration?: number | null; elementName?: string; @@ -29,7 +37,7 @@ export class UmbNotificationContext { * @return {*} {UmbNotificationHandler} * @memberof UmbNotificationContext */ - private _open(options: UmbNotificationOptions): UmbNotificationHandler { + private _open(options: UmbNotificationOptions): UmbNotificationHandler { const notificationHandler = new UmbNotificationHandler(options); notificationHandler.element.addEventListener('closed', () => this._handleClosed(notificationHandler)); @@ -66,7 +74,7 @@ export class UmbNotificationContext { */ public peek( color: UmbNotificationColor, - options: UmbNotificationOptions + options: UmbNotificationOptions ): UmbNotificationHandler { return this._open({ color, ...options }); } @@ -80,10 +88,10 @@ export class UmbNotificationContext { */ public stay( color: UmbNotificationColor, - options: UmbNotificationOptions + options: UmbNotificationOptions ): UmbNotificationHandler { return this._open({ ...options, color, duration: null }); } } -export const UMB_NOTIFICATION_CONTEXT_TOKEN = new UmbContextToken(UmbNotificationContext.name); +export const UMB_NOTIFICATION_CONTEXT_TOKEN = new UmbContextToken('UmbNotificationContext'); diff --git a/src/Umbraco.Web.UI.Client/libs/resources/resource.controller.ts b/src/Umbraco.Web.UI.Client/libs/resources/resource.controller.ts index d0ac8ed130..26c8a481a2 100644 --- a/src/Umbraco.Web.UI.Client/libs/resources/resource.controller.ts +++ b/src/Umbraco.Web.UI.Client/libs/resources/resource.controller.ts @@ -2,7 +2,6 @@ import { UmbNotificationOptions, UmbNotificationContext, - UmbNotificationDefaultData, UMB_NOTIFICATION_CONTEXT_TOKEN, } from '@umbraco-cms/notification'; import { ApiError, CancelablePromise, ProblemDetailsModel } from '@umbraco-cms/backend-api'; @@ -57,9 +56,9 @@ export class UmbResourceController extends UmbController { */ static async tryExecute(promise: Promise): Promise> { try { - return { data: await promise }; + return {data: await promise}; } catch (e) { - return { error: UmbResourceController.toProblemDetailsModel(e) }; + return {error: UmbResourceController.toProblemDetailsModel(e)}; } } @@ -67,17 +66,17 @@ export class UmbResourceController extends UmbController { * Wrap the {execute} function in a try/catch block and return the result. * If the executor function throws an error, then show the details in a notification. */ - async tryExecuteAndNotify(options?: UmbNotificationOptions): Promise> { - const { data, error } = await UmbResourceController.tryExecute(this.#promise); + async tryExecuteAndNotify(options?: UmbNotificationOptions): Promise> { + const {data, error} = await UmbResourceController.tryExecute(this.#promise); if (error) { - const data: UmbNotificationDefaultData = { - headline: error.title ?? 'Server Error', - message: error.detail ?? 'Something went wrong', - }; - if (this.#notificationContext) { - this.#notificationContext?.peek('danger', { data, ...options }); + this.#notificationContext?.peek('danger', { + data: { + headline: error.title ?? 'Server Error', + message: error.detail ?? 'Something went wrong' + }, ...options + }); } else { console.group('UmbResourceController'); console.error(error); @@ -85,7 +84,7 @@ export class UmbResourceController extends UmbController { } } - return { data, error }; + return {data, error}; } /** diff --git a/src/Umbraco.Web.UI.Client/libs/utils/rollup.config.js b/src/Umbraco.Web.UI.Client/libs/utils/rollup.config.js new file mode 100644 index 0000000000..945c0afe88 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/libs/utils/rollup.config.js @@ -0,0 +1,4 @@ +import config from '../../utils/rollup.config.js'; +export default { + ...config, +}; diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 5da420d380..7979cc6c6e 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -27,6 +27,7 @@ "@mdx-js/react": "^2.2.1", "@open-wc/testing": "^3.1.7", "@playwright/test": "^1.30.0", + "@rollup/plugin-json": "^6.0.0", "@storybook/addon-a11y": "^7.0.0-beta.59", "@storybook/addon-actions": "^7.0.0-beta.59", "@storybook/addon-essentials": "^7.0.0-beta.59", @@ -3046,6 +3047,60 @@ "fsevents": "2.3.2" } }, + "node_modules/@rollup/plugin-json": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.0.0.tgz", + "integrity": "sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-json/node_modules/@rollup/pluginutils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", + "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-json/node_modules/@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "dev": true + }, + "node_modules/@rollup/plugin-json/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, "node_modules/@rollup/plugin-node-resolve": { "version": "13.3.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz", @@ -20671,6 +20726,40 @@ "playwright-core": "1.31.1" } }, + "@rollup/plugin-json": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.0.0.tgz", + "integrity": "sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^5.0.1" + }, + "dependencies": { + "@rollup/pluginutils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", + "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", + "dev": true, + "requires": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + } + }, + "@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "dev": true + }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + } + } + }, "@rollup/plugin-node-resolve": { "version": "13.3.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz", diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 243a0a1ac7..16befe9624 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -29,8 +29,8 @@ "dev": "vite", "build": "tsc && vite build --mode staging", "build:for:static": "tsc && vite build", - "build:for:cms": "tsc && vite build -c vite.cms.config.ts", - "build:for:cms:watch": "npm run build:for:cms -- --watch", + "build:for:cms": "tsc && vite build -c vite.cms.config.ts && node utils/build-libs.js", + "build:for:cms:watch": "vite build -c vite.cms.config.ts --watch", "preview": "vite preview --open", "test": "web-test-runner --coverage", "test:watch": "web-test-runner --watch", @@ -75,6 +75,7 @@ "@mdx-js/react": "^2.2.1", "@open-wc/testing": "^3.1.7", "@playwright/test": "^1.30.0", + "@rollup/plugin-json": "^6.0.0", "@storybook/addon-a11y": "^7.0.0-beta.59", "@storybook/addon-actions": "^7.0.0-beta.59", "@storybook/addon-essentials": "^7.0.0-beta.59", diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.store.ts index 1906d62454..7b38c47233 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.store.ts @@ -19,7 +19,7 @@ export class UmbDocumentTypeStore extends UmbStoreBase { * @memberof UmbDocumentTypeStore */ constructor(host: UmbControllerHostInterface) { - super(host, UmbDocumentTypeStore.name); + super(host, UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN.toString()); } /** @@ -51,5 +51,5 @@ export class UmbDocumentTypeStore extends UmbStoreBase { } export const UMB_DOCUMENT_TYPE_STORE_CONTEXT_TOKEN = new UmbContextToken( - UmbDocumentTypeStore.name + 'UmbDocumentTypeStore' ); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.tree.store.ts index b18c51f11a..0f1e5b38ca 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/document-types/repository/document-type.tree.store.ts @@ -21,5 +21,5 @@ export class UmbDocumentTypeTreeStore extends UmbTreeStoreBase { } export const UMB_DOCUMENT_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken( - UmbDocumentTypeTreeStore.name + 'UmbDocumentTypeTreeStore' ); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.store.ts index 274ef33130..9f7df54d24 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.store.ts @@ -4,6 +4,8 @@ import { ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; +export const UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbDocumentStore'); + /** * @export * @class UmbDocumentDetailStore @@ -19,7 +21,7 @@ export class UmbDocumentStore extends UmbStoreBase { * @memberof UmbDocumentDetailStore */ constructor(host: UmbControllerHostInterface) { - super(host, UmbDocumentStore.name); + super(host, UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN.toString()); } /** @@ -49,5 +51,3 @@ export class UmbDocumentStore extends UmbStoreBase { this.#data.remove(uniques); } } - -export const UMB_DOCUMENT_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken(UmbDocumentStore.name); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.tree.store.ts index 8cc4cd5aa3..71a563ed34 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/documents/documents/repository/document.tree.store.ts @@ -20,5 +20,5 @@ export class UmbDocumentTreeStore extends UmbTreeStoreBase { } export const UMB_DOCUMENT_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken( - UmbDocumentTreeStore.name + 'UmbDocumentTreeStore' ); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/media-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/media-type.detail.store.ts index c6dfb6cbd7..841ca9822a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/media-type.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/media-type.detail.store.ts @@ -16,7 +16,7 @@ export class UmbMediaTypeDetailStore #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { - super(host, UmbMediaTypeDetailStore.name); + super(host, UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString()); } append(mediaType: MediaTypeDetails) { @@ -25,9 +25,9 @@ export class UmbMediaTypeDetailStore remove(uniques: string[]) { this.#data.remove(uniques); - } + } } export const UMB_MEDIA_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken( - UmbMediaTypeDetailStore.name -); \ No newline at end of file + 'UmbMediaTypeDetailStore' +); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/media-type.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/media-type.tree.store.ts index fa1d2ab1dc..0f82fd5a61 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/media-type.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media-types/repository/media-type.tree.store.ts @@ -21,5 +21,5 @@ export class UmbMediaTypeTreeStore extends UmbTreeStoreBase { } export const UMB_MEDIA_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken( - UmbMediaTypeTreeStore.name + 'UmbMediaTypeTreeStore' ); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.detail.store.ts index 7519f79e8f..9130ba65bf 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.detail.store.ts @@ -4,6 +4,8 @@ import { ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; +export const UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbMediaDetailStore'); + /** * @export * @class UmbMediaDetailStore @@ -19,7 +21,7 @@ export class UmbMediaDetailStore extends UmbStoreBase { * @memberof UmbMediaDetailStore */ constructor(host: UmbControllerHostInterface) { - super(host, UmbMediaDetailStore.name); + super(host, UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN.toString()); } /** @@ -40,5 +42,3 @@ export class UmbMediaDetailStore extends UmbStoreBase { this.#data.remove(uniques); } } - -export const UMB_MEDIA_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken(UmbMediaDetailStore.name); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.tree.store.ts index 572b71a06f..07299c5e8f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/media/media/repository/media.tree.store.ts @@ -4,6 +4,8 @@ import { ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; +export const UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbMediaTreeStore'); + /** * @export * @class UmbMediaTreeStore @@ -87,5 +89,3 @@ export class UmbMediaTreeStore extends UmbStoreBase { return this.#data.getObservablePart((items) => items.filter((item) => keys.includes(item.key ?? ''))); } } - -export const UMB_MEDIA_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken(UmbMediaTreeStore.name); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/member-group.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/member-group.detail.store.ts index 64c5973d5d..ce6053cbd6 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/member-group.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/member-group.detail.store.ts @@ -4,6 +4,10 @@ import { ArrayState } from '@umbraco-cms/observable-api'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; import { UmbStoreBase } from '@umbraco-cms/store'; +export const UMB_MEMBER_GROUP_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken( + 'UmbMemberGroupDetailStore' +); + /** * @export * @class UmbMemberGroupDetailStore @@ -16,7 +20,7 @@ export class UmbMemberGroupDetailStore #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { - super(host, UmbMemberGroupDetailStore.name); + super(host, UMB_MEMBER_GROUP_DETAIL_STORE_CONTEXT_TOKEN.toString()); } append(memberGroup: MemberGroupDetails) { @@ -25,9 +29,5 @@ export class UmbMemberGroupDetailStore remove(uniques: string[]) { this.#data.remove(uniques); - } + } } - -export const UMB_MEMBER_GROUP_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken( - UmbMemberGroupDetailStore.name -); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/member-group.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/member-group.tree.store.ts index a1e5a29659..bad706d967 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/member-group.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-groups/repository/member-group.tree.store.ts @@ -91,5 +91,5 @@ export class UmbMemberGroupTreeStore extends UmbStoreBase { } export const UMB_MEMBER_GROUP_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken( - UmbMemberGroupTreeStore.name + 'UmbMemberGroupTreeStore' ); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/member-type.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/member-type.detail.store.ts index bba1a580fa..ebeb8af837 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/member-type.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/member-type.detail.store.ts @@ -16,7 +16,7 @@ export class UmbMemberTypeDetailStore #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { - super(host, UmbMemberTypeDetailStore.name); + super(host, UMB_MEMBER_TYPE_DETAIL_STORE_CONTEXT_TOKEN.toString()); } append(MemberType: MemberTypeDetails) { @@ -25,9 +25,9 @@ export class UmbMemberTypeDetailStore remove(uniques: string[]) { this.#data.remove(uniques); - } + } } export const UMB_MEMBER_TYPE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken( - UmbMemberTypeDetailStore.name -); \ No newline at end of file + 'UmbMemberTypeDetailStore' +); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/member-type.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/member-type.tree.store.ts index 7e3322625d..3b87c4988f 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/member-type.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/member-types/repository/member-type.tree.store.ts @@ -16,5 +16,5 @@ export class UmbMemberTypeTreeStore extends UmbTreeStoreBase { } export const UMB_MEMBER_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken( - UmbMemberTypeTreeStore.name + 'UmbMemberTypeTreeStore' ); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/members/members/repository/member.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/members/members/repository/member.tree.store.ts index dad2c697c0..c3ea8a4e18 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/members/members/repository/member.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/members/members/repository/member.tree.store.ts @@ -4,6 +4,8 @@ import { ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import type { UmbControllerHostInterface } from '@umbraco-cms/controller'; +export const UMB_MEMBER_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbMemberTreeStore'); + /** * @export * @class UmbMemberTreeStore @@ -89,5 +91,3 @@ export class UmbMemberTreeStore extends UmbStoreBase { return this.#data.getObservablePart((items) => items.filter((item) => keys.includes(item.key ?? ''))); } } - -export const UMB_MEMBER_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken(UmbMemberTreeStore.name); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/package.repository.ts b/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/package.repository.ts index 61c8e33cdd..f28c63c378 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/package.repository.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/package.repository.ts @@ -3,6 +3,8 @@ import { UmbPackageServerDataSource } from './sources/package.server.data'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; import { UmbContextConsumerController } from '@umbraco-cms/context-api'; import { ManifestBase } from '@umbraco-cms/extensions-registry'; +import { isManifestJSType } from "@umbraco-cms/extensions-api"; +import { OpenAPI } from "@umbraco-cms/backend-api"; // TODO: Figure out if we should base stores like this on something more generic for "collections" rather than trees. @@ -14,6 +16,7 @@ export class UmbPackageRepository { #init!: Promise; #packageStore?: UmbPackageStore; #packageSource: UmbPackageServerDataSource; + #apiBaseUrl = OpenAPI.BASE; constructor(host: UmbControllerHostInterface) { this.#packageSource = new UmbPackageServerDataSource(host); @@ -48,6 +51,18 @@ export class UmbPackageRepository { // Crudely validate that the extension at least follows a basic manifest structure // Idea: Use `Zod` to validate the manifest if (this.isManifestBase(e)) { + + /** + * Crude check to see if extension is of type "js" since it is safe to assume we do not + * need to load any other types of extensions in the backoffice (we need a js file to load) + */ + if (isManifestJSType(e)) { + // Add API base url if the js path is relative + if (!e.js.startsWith('http')) { + e.js = `${this.#apiBaseUrl}${e.js}`; + } + } + extensions.push(e); } }); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/package.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/package.store.ts index f581b2045d..18120d46a9 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/package.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/package.store.ts @@ -6,6 +6,8 @@ import type { ManifestBase, UmbPackage } from '@umbraco-cms/models'; import type { PackageMigrationStatusModel } from '@umbraco-cms/backend-api'; import { ArrayState } from '@umbraco-cms/observable-api'; +export const UMB_PACKAGE_STORE_TOKEN = new UmbContextToken('UmbPackageStore'); + /** * Store for Packages * @export @@ -39,7 +41,7 @@ export class UmbPackageStore extends UmbStoreBase { * @memberof PackageStore */ constructor(host: UmbControllerHostInterface) { - super(host, UmbPackageStore.name); + super(host, UMB_PACKAGE_STORE_TOKEN.toString()); } /** @@ -58,5 +60,3 @@ export class UmbPackageStore extends UmbStoreBase { this.#migrations.append(migrations); } } - -export const UMB_PACKAGE_STORE_TOKEN = new UmbContextToken(UmbPackageStore.name); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/server-extension.controller.ts b/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/server-extension.controller.ts index 0073576b89..74a3db7ea0 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/server-extension.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/server-extension.controller.ts @@ -1,15 +1,16 @@ import { Subject, takeUntil } from 'rxjs'; import { UmbPackageRepository } from './package.repository'; import { UmbController, UmbControllerHostInterface } from '@umbraco-cms/controller'; -import { isManifestJSType, UmbExtensionRegistry } from '@umbraco-cms/extensions-api'; +import { UmbExtensionRegistry } from '@umbraco-cms/extensions-api'; export class UmbServerExtensionController extends UmbController { + #host: UmbControllerHostInterface; #unobserve = new Subject(); #repository: UmbPackageRepository; constructor(host: UmbControllerHostInterface, private readonly extensionRegistry: UmbExtensionRegistry) { super(host, UmbServerExtensionController.name); - + this.#host = host; this.#repository = new UmbPackageRepository(host); } @@ -32,13 +33,7 @@ export class UmbServerExtensionController extends UmbController { ) .subscribe((extensions) => { extensions.forEach((extension) => { - /** - * Crude check to see if extension is of type "js" since it is safe to assume we do not - * need to load any other types of extensions in the backoffice (we need a js file to load) - */ - if (isManifestJSType(extension)) { - this.extensionRegistry.register(extension); - } + this.extensionRegistry.register(extension, this.#host); }); }); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/sources/package.server.data.ts b/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/sources/package.server.data.ts index e2bdd212fa..251f76608c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/sources/package.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/packages/repository/sources/package.server.data.ts @@ -1,8 +1,6 @@ import { PackageResource } from '@umbraco-cms/backend-api'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; -import type { DataSourceResponse, UmbPackage } from '@umbraco-cms/models'; import { tryExecuteAndNotify } from '@umbraco-cms/resources'; -import { umbracoPath } from '@umbraco-cms/utils'; /** * Data source for packages from the server @@ -15,11 +13,10 @@ export class UmbPackageServerDataSource { * Get the root items from the server * @memberof UmbPackageServerDataSource */ - getRootItems(): Promise> { - // TODO: Use real resource when available + getRootItems() { return tryExecuteAndNotify( this.host, - fetch(umbracoPath('/package/manifest')).then((res) => res.json()) + PackageResource.getPackageManifest() ); } diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/health-check/health-check-dashboard.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/health-check/health-check-dashboard.context.ts index f4c41f1cbc..73f987fdea 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/health-check/health-check-dashboard.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/health-check/health-check-dashboard.context.ts @@ -35,5 +35,5 @@ export class UmbHealthCheckDashboardContext { } export const UMB_HEALTHCHECK_DASHBOARD_CONTEXT_TOKEN = new UmbContextToken( - UmbHealthCheckDashboardContext.name + 'UmbHealthCheckDashboardContext' ); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/health-check/health-check.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/health-check/health-check.context.ts index c810211f9a..920af65be7 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/health-check/health-check.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/dashboards/health-check/health-check.context.ts @@ -41,4 +41,4 @@ export class UmbHealthCheckContext { } } -export const UMB_HEALTHCHECK_CONTEXT_TOKEN = new UmbContextToken(UmbHealthCheckContext.name); +export const UMB_HEALTHCHECK_CONTEXT_TOKEN = new UmbContextToken('UmbHealthCheckContext'); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.store.ts index 4cd7c8dad1..d6d8ef2d94 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.store.ts @@ -4,6 +4,8 @@ import { ArrayState } from '@umbraco-cms/observable-api'; import { UmbStoreBase } from '@umbraco-cms/store'; import { UmbControllerHostInterface } from '@umbraco-cms/controller'; +export const UMB_DATA_TYPE_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbDataTypeStore'); + /** * @export * @class UmbDataTypeStore @@ -19,7 +21,7 @@ export class UmbDataTypeStore extends UmbStoreBase { * @memberof UmbDataTypeStore */ constructor(host: UmbControllerHostInterface) { - super(host, UmbDataTypeStore.name); + super(host, UMB_DATA_TYPE_STORE_CONTEXT_TOKEN.toString()); } /** @@ -49,5 +51,3 @@ export class UmbDataTypeStore extends UmbStoreBase { this.#data.remove(uniques); } } - -export const UMB_DATA_TYPE_STORE_CONTEXT_TOKEN = new UmbContextToken(UmbDataTypeStore.name); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.tree.store.ts index 6cf869e5e1..a29ae02cf8 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/data-types/repository/data-type.tree.store.ts @@ -21,5 +21,5 @@ export class UmbDataTypeTreeStore extends UmbTreeStoreBase { } export const UMB_DATA_TYPE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken( - UmbDataTypeTreeStore.name + 'UmbDataTypeTreeStore' ); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/app-language-select/app-language.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/app-language-select/app-language.context.ts index 82c2d32649..867f876579 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/app-language-select/app-language.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/app-language-select/app-language.context.ts @@ -47,4 +47,4 @@ export class UmbAppLanguageContext { } } -export const UMB_APP_LANGUAGE_CONTEXT_TOKEN = new UmbContextToken(UmbAppLanguageContext.name); +export const UMB_APP_LANGUAGE_CONTEXT_TOKEN = new UmbContextToken('UmbAppLanguageContext'); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/language.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/language.store.ts index 7f13331aca..3da9a5a38a 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/language.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/settings/languages/repository/language.store.ts @@ -4,6 +4,8 @@ import { UmbControllerHostInterface } from '@umbraco-cms/controller'; import { ArrayState } from '@umbraco-cms/observable-api'; import { LanguageModel } from '@umbraco-cms/backend-api'; +export const UMB_LANGUAGE_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbLanguageStore'); + /** * @export * @class UmbLanguageStore @@ -15,7 +17,7 @@ export class UmbLanguageStore extends UmbStoreBase { data = this.#data.asObservable(); constructor(host: UmbControllerHostInterface) { - super(host, UmbLanguageStore.name); + super(host, UMB_LANGUAGE_STORE_CONTEXT_TOKEN.toString()); } append(language: LanguageModel) { @@ -32,4 +34,3 @@ export class UmbLanguageStore extends UmbStoreBase { } } -export const UMB_LANGUAGE_STORE_CONTEXT_TOKEN = new UmbContextToken(UmbLanguageStore.name); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts index 27e008d1bf..7ad8352aa4 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/collection/collection.context.ts @@ -175,5 +175,5 @@ export class UmbCollectionContext< } export const UMB_COLLECTION_CONTEXT_TOKEN = new UmbContextToken>( - UmbCollectionContext.name + 'UmbCollectionContext' ); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar/section-sidebar.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar/section-sidebar.context.ts index d869159a1e..c926095689 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar/section-sidebar.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section-sidebar/section-sidebar.context.ts @@ -43,5 +43,5 @@ export class UmbSectionSidebarContext { } export const UMB_SECTION_SIDEBAR_CONTEXT_TOKEN = new UmbContextToken( - UmbSectionSidebarContext.name + 'UmbSectionSidebarContext' ); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts index 884797ca62..74be5bbd8d 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/section/section.context.ts @@ -53,4 +53,4 @@ export class UmbSectionContext { } } -export const UMB_SECTION_CONTEXT_TOKEN = new UmbContextToken(UmbSectionContext.name); +export const UMB_SECTION_CONTEXT_TOKEN = new UmbContextToken('UmbSectionContext'); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/context-menu/tree-context-menu-page.service.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/context-menu/tree-context-menu-page.service.ts index 08adff9284..5194dd96f5 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/context-menu/tree-context-menu-page.service.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/context-menu/tree-context-menu-page.service.ts @@ -67,7 +67,7 @@ export class UmbTreeContextMenuPageService extends UmbLitElement { } export const UMB_TREE_CONTEXT_MENU_PAGE_SERVICE_CONTEXT_TOKEN = new UmbContextToken( - UmbTreeContextMenuService.name + 'UmbTreeContextMenuService' ); declare global { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/context-menu/tree-context-menu.service.ts b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/context-menu/tree-context-menu.service.ts index 5b6e322682..dd057f5bf3 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/context-menu/tree-context-menu.service.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/shared/components/tree/context-menu/tree-context-menu.service.ts @@ -94,7 +94,7 @@ export class UmbTreeContextMenuService extends UmbLitElement { } export const UMB_TREE_CONTEXT_MENU_SERVICE_CONTEXT_TOKEN = new UmbContextToken( - UmbTreeContextMenuService.name + 'UmbTreeContextMenuService' ); declare global { diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/tree/data/template.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/tree/data/template.tree.store.ts index e2fc0588da..448bde7b44 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/tree/data/template.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/tree/data/template.tree.store.ts @@ -2,6 +2,10 @@ import { UmbContextToken } from '@umbraco-cms/context-api'; import { UmbTreeStoreBase } from '@umbraco-cms/store'; import type { UmbControllerHostInterface } from '@umbraco-cms/controller'; +export const UMB_TEMPLATE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken( + 'UmbTemplateTreeStore' +); + /** * @export * @class UmbTemplateTreeStore @@ -18,7 +22,3 @@ export class UmbTemplateTreeStore extends UmbTreeStoreBase { super(host, UMB_TEMPLATE_TREE_STORE_CONTEXT_TOKEN.toString()); } } - -export const UMB_TEMPLATE_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken( - UmbTemplateTreeStore.name -); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/workspace/data/template.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/workspace/data/template.detail.store.ts index f12b0bcf27..8fa732967e 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/workspace/data/template.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/templating/templates/workspace/data/template.detail.store.ts @@ -19,7 +19,7 @@ export class UmbTemplateDetailStore extends UmbStoreBase { * @memberof UmbTemplateDetailStore */ constructor(host: UmbControllerHostInterface) { - super(host, UmbTemplateDetailStore.name); + super(host, UMB_TEMPLATE_DETAIL_STORE_CONTEXT_TOKEN.toString()); } /** @@ -42,5 +42,5 @@ export class UmbTemplateDetailStore extends UmbStoreBase { } export const UMB_TEMPLATE_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken( - UmbTemplateDetailStore.name + 'UmbTemplateDetailStore' ); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/dictionary.detail.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/dictionary.detail.store.ts index 9139f184b4..e9a7962335 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/dictionary.detail.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/dictionary.detail.store.ts @@ -16,7 +16,7 @@ export class UmbDictionaryDetailStore #data = new ArrayState([], (x) => x.key); constructor(host: UmbControllerHostInterface) { - super(host, UmbDictionaryDetailStore.name); + super(host, UMB_DICTIONARY_DETAIL_STORE_CONTEXT_TOKEN.toString()); } append(dictionary: DictionaryDetails) { @@ -25,9 +25,9 @@ export class UmbDictionaryDetailStore remove(uniques: string[]) { this.#data.remove(uniques); - } + } } export const UMB_DICTIONARY_DETAIL_STORE_CONTEXT_TOKEN = new UmbContextToken( - UmbDictionaryDetailStore.name -); \ No newline at end of file + 'UmbDictionaryDetailStore' +); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/dictionary.tree.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/dictionary.tree.store.ts index d71ec0afcf..8a3dcb7310 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/dictionary.tree.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/translation/dictionary/repository/dictionary.tree.store.ts @@ -9,7 +9,7 @@ import { UmbControllerHostInterface } from '@umbraco-cms/controller'; * @description - Tree Data Store for Dictionary */ export class UmbDictionaryTreeStore extends UmbTreeStoreBase { - + /** * Creates an instance of UmbDictionaryTreeStore. * @param {UmbControllerHostInterface} host @@ -21,5 +21,5 @@ export class UmbDictionaryTreeStore extends UmbTreeStoreBase { } export const UMB_DICTIONARY_TREE_STORE_CONTEXT_TOKEN = new UmbContextToken( - UmbDictionaryTreeStore.name -); \ No newline at end of file + 'UmbDictionaryTreeStore' +); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user-history.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user-history.store.ts index 9a7468fd15..f16efb7640 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user-history.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user-history.store.ts @@ -51,5 +51,5 @@ export class UmbCurrentUserHistoryStore { } export const UMB_CURRENT_USER_HISTORY_STORE_CONTEXT_TOKEN = new UmbContextToken( - UmbCurrentUserHistoryStore.name + 'UmbCurrentUserHistoryStore' ); diff --git a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user.store.ts b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user.store.ts index 6f6dcdc54e..024bf6b78c 100644 --- a/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user.store.ts +++ b/src/Umbraco.Web.UI.Client/src/backoffice/users/current-user/current-user.store.ts @@ -4,6 +4,8 @@ import type { UserDetails } from '@umbraco-cms/models'; import { UmbContextToken } from '@umbraco-cms/context-api'; import { ObjectState } from '@umbraco-cms/observable-api'; +export const UMB_CURRENT_USER_STORE_CONTEXT_TOKEN = new UmbContextToken('UmbCurrentUserStore'); + export class UmbCurrentUserStore { //TODO: Temp solution to get a current user. Replace when we have a real user service private _currentUser = new ObjectState(umbUsersData.getAll()[0]); @@ -30,4 +32,3 @@ export class UmbCurrentUserStore { } } -export const UMB_CURRENT_USER_STORE_CONTEXT_TOKEN = new UmbContextToken(UmbCurrentUserStore.name); diff --git a/src/Umbraco.Web.UI.Client/src/core/mocks/domains/manifests.handlers.ts b/src/Umbraco.Web.UI.Client/src/core/mocks/domains/manifests.handlers.ts index a5f0be9773..47104cf642 100644 --- a/src/Umbraco.Web.UI.Client/src/core/mocks/domains/manifests.handlers.ts +++ b/src/Umbraco.Web.UI.Client/src/core/mocks/domains/manifests.handlers.ts @@ -1,13 +1,14 @@ + import { rest } from 'msw'; import { umbracoPath } from '@umbraco-cms/utils'; -import type { PagedManifestsResponse } from '@umbraco-cms/models'; +import type { PackageManifestResponse } from '@umbraco-cms/models'; export const manifestDevelopmentHandler = rest.get(umbracoPath('/package/manifest'), (_req, res, ctx) => { return res( // Respond with a 200 status code ctx.status(200), - ctx.json([ + ctx.json([ { name: 'Named Package', version: '1.0.0', @@ -71,6 +72,6 @@ export const manifestEmptyHandler = rest.get(umbracoPath('/package/manifest'), ( return res( // Respond with a 200 status code ctx.status(200), - ctx.json([]) + ctx.json([]) ); }); diff --git a/src/Umbraco.Web.UI.Client/src/core/modal/modal.context.ts b/src/Umbraco.Web.UI.Client/src/core/modal/modal.context.ts index 21fe1aed0b..1bf8696341 100644 --- a/src/Umbraco.Web.UI.Client/src/core/modal/modal.context.ts +++ b/src/Umbraco.Web.UI.Client/src/core/modal/modal.context.ts @@ -34,7 +34,6 @@ export interface UmbModalOptions { data?: UmbModalData; } -// TODO: rename to UmbModalContext // TODO: we should find a way to easily open a modal without adding custom methods to this context. It would result in a better separation of concerns. // TODO: move all layouts into their correct "silo" folders. User picker should live with users etc. export class UmbModalContext { @@ -230,4 +229,4 @@ export class UmbModalContext { } } -export const UMB_MODAL_CONTEXT_TOKEN = new UmbContextToken(UmbModalContext.name); +export const UMB_MODAL_CONTEXT_TOKEN = new UmbContextToken('UmbModalContext'); diff --git a/src/Umbraco.Web.UI.Client/src/installer/installer.context.ts b/src/Umbraco.Web.UI.Client/src/installer/installer.context.ts index c4161e4255..d51f3d2c28 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/installer.context.ts +++ b/src/Umbraco.Web.UI.Client/src/installer/installer.context.ts @@ -130,4 +130,4 @@ export class UmbInstallerContext { } } -export const UMB_INSTALLER_CONTEXT_TOKEN = new UmbContextToken(UmbInstallerContext.name); +export const UMB_INSTALLER_CONTEXT_TOKEN = new UmbContextToken('UmbInstallerContext'); diff --git a/src/Umbraco.Web.UI.Client/src/stories/umbelement.mdx b/src/Umbraco.Web.UI.Client/src/stories/umbelement.mdx index 63324c4f92..025a2235d8 100644 --- a/src/Umbraco.Web.UI.Client/src/stories/umbelement.mdx +++ b/src/Umbraco.Web.UI.Client/src/stories/umbelement.mdx @@ -30,7 +30,7 @@ this.consumeContext('requestThisContextAlias', (context) => { Or use the UmbContextToken type to define the type of the context, like this ```typescript -const contextAlias = new UmbContextToken('description of context for debugging purposes'); +const contextAlias = new UmbContextToken('SomeTypeAlias', 'description of context for debugging purposes'); this.consumeContext(contextAlias, (context) => { // Notice this is a subscription, as context might change or a new one appears, but the value is strongly typed diff --git a/src/Umbraco.Web.UI.Client/utils/build-libs.js b/src/Umbraco.Web.UI.Client/utils/build-libs.js new file mode 100644 index 0000000000..e85e2fa4d4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/utils/build-libs.js @@ -0,0 +1,67 @@ +import * as fs from 'fs'; +import { exec } from 'child_process'; + +const libsDistFolder = '../Umbraco.Cms.StaticAssets/wwwroot/umbraco/backoffice/libs'; +const libs = fs.readdirSync('./libs'); + +for (let i = 0; i < libs.length; i++) { + const lib = libs[i]; + const libFolder = './libs/' + lib; + if (fs.statSync(libFolder).isDirectory()) { + const libPackage = libFolder + '/rollup.config.js'; + if (!fs.existsSync(libPackage)) { + continue; + } + + console.log('Installing ' + lib + '...'); + exec('npx rollup -c rollup.config.js', { cwd: libFolder }, function (error) { + if (error) { + console.error('Error installing ' + lib + '!'); + console.error(error); + } else { + console.log('Installed ' + lib + '.'); + + copyDistFromLib(lib, `${libFolder}/dist`); + } + }); + } +} + +function copyDistFromLib(libName, distPath) { + console.log(`Copying ${libName} to StaticAssets`); + const targetFolder = `${libsDistFolder}/${libName}`; + + fs.cp(distPath, targetFolder, { recursive: true }, function (err) { + if (err) { + console.error(`Error copying ${libName}`); + console.error(err); + } else { + console.log(`Copied ${libName}`); + findAndCopyTypesForLib(libName); + } + }); +} + +/** + * Look in the ./types/libs folder for a folder with the same name as the {libName} parameter + * and copy those types into the `${libsDistFolder}/${libName}` folder. + * Wrap the types from the index.d.ts file as a new module called "@umbraco-cms/{libName}". + */ +function findAndCopyTypesForLib(libName) { + console.log('Installing types for', libName); + const typesFolder = './types/libs'; + const libTypesFolder = `${typesFolder}/${libName}`; + if (fs.existsSync(libTypesFolder)) { + const libTypesTargetFolder = `${libsDistFolder}/${libName}`; + fs.cpSync(libTypesFolder, `${libTypesTargetFolder}/types`, { recursive: true }); + fs.writeFileSync(`${libTypesTargetFolder}/index.d.ts`, wrapLibTypeContent(libName), {}); + } +} + +function wrapLibTypeContent(libName) { + return ` + import * as lib from './types'; + declare module "@umbraco-cms/${libName}" { + export = lib; + }`; +} diff --git a/src/Umbraco.Web.UI.Client/utils/rollup.config.js b/src/Umbraco.Web.UI.Client/utils/rollup.config.js index 3fee640fc8..e096f7cfb6 100644 --- a/src/Umbraco.Web.UI.Client/utils/rollup.config.js +++ b/src/Umbraco.Web.UI.Client/utils/rollup.config.js @@ -1,14 +1,15 @@ import esbuild from 'rollup-plugin-esbuild'; -//import { nodeResolve } from '@rollup/plugin-node-resolve'; +import pluginJson from '@rollup/plugin-json'; +import { nodeResolve } from '@rollup/plugin-node-resolve'; /** @type {import('rollup').RollupOptions} */ export default { input: 'index.ts', - external: [/^@umbraco-cms\//, /^@umbraco-ui\//, /^lit/, /^rxjs/], + external: [/^@umbraco-cms\//], output: { file: 'dist/index.js', format: 'es', sourcemap: true, }, - plugins: [esbuild({ sourceMap: true })], + plugins: [nodeResolve(), pluginJson(), esbuild({ sourceMap: true })], };