Chore: Temp solution to remove user circular imports (#19021)

* remove user and document/media circular imports

* fix code order

* change MAX_CIRCULAR_DEPENDENCIES

* remove all comments from content before checking for imports

* fix self import

* fix self imports
This commit is contained in:
Mads Rasmussen
2025-04-15 15:18:14 +02:00
committed by GitHub
parent dab3975d2e
commit fcc8e2fe3a
12 changed files with 83 additions and 46 deletions

View File

@@ -12,7 +12,7 @@ import { join } from 'path';
//const __dirname = import.meta.dirname; //const __dirname = import.meta.dirname;
// Adjust this number as needed. // Adjust this number as needed.
const MAX_CIRCULAR_DEPENDENCIES = 4; const MAX_CIRCULAR_DEPENDENCIES = 1;
const IS_GITHUB_ACTIONS = process.env.GITHUB_ACTIONS === 'true'; const IS_GITHUB_ACTIONS = process.env.GITHUB_ACTIONS === 'true';
const IS_AZURE_PIPELINES = process.env.TF_BUILD === 'true'; const IS_AZURE_PIPELINES = process.env.TF_BUILD === 'true';

View File

@@ -3,7 +3,7 @@ import path from 'path';
import { createImportMap } from '../importmap/index.js'; import { createImportMap } from '../importmap/index.js';
const ILLEGAL_CORE_IMPORTS_THRESHOLD = 6; const ILLEGAL_CORE_IMPORTS_THRESHOLD = 6;
const SELF_IMPORTS_THRESHOLD = 7; const SELF_IMPORTS_THRESHOLD = 4;
const clientProjectRoot = path.resolve(import.meta.dirname, '../../'); const clientProjectRoot = path.resolve(import.meta.dirname, '../../');
const modulePrefix = '@umbraco-cms/backoffice/'; const modulePrefix = '@umbraco-cms/backoffice/';

View File

@@ -3,11 +3,11 @@ import { UmbMediaTreeRepository } from '../../tree/media-tree.repository.js';
import { UMB_MEDIA_ROOT_ENTITY_TYPE } from '../../entity.js'; import { UMB_MEDIA_ROOT_ENTITY_TYPE } from '../../entity.js';
import type { UmbMediaTreeItemModel, UmbMediaSearchItemModel, UmbMediaItemModel } from '../../types.js'; import type { UmbMediaTreeItemModel, UmbMediaSearchItemModel, UmbMediaItemModel } from '../../types.js';
import { UmbMediaSearchProvider } from '../../search/index.js'; import { UmbMediaSearchProvider } from '../../search/index.js';
import type { UmbDropzoneMediaElement } from '../../dropzone/index.js';
import type { UmbMediaPathModel } from './types.js'; import type { UmbMediaPathModel } from './types.js';
import type { UmbMediaPickerFolderPathElement } from './components/media-picker-folder-path.element.js'; import type { UmbMediaPickerFolderPathElement } from './components/media-picker-folder-path.element.js';
import type { UmbMediaPickerModalData, UmbMediaPickerModalValue } from './media-picker-modal.token.js'; import type { UmbMediaPickerModalData, UmbMediaPickerModalValue } from './media-picker-modal.token.js';
import type { UmbDropzoneChangeEvent, UmbUploadableItem } from '@umbraco-cms/backoffice/dropzone'; import type { UmbDropzoneChangeEvent, UmbUploadableItem } from '@umbraco-cms/backoffice/dropzone';
import type { UmbDropzoneMediaElement } from '@umbraco-cms/backoffice/media';
import { import {
css, css,
html, html,

View File

@@ -12,13 +12,12 @@ import type {
UmbTableItem, UmbTableItem,
UmbTableSelectedEvent, UmbTableSelectedEvent,
} from '@umbraco-cms/backoffice/components'; } from '@umbraco-cms/backoffice/components';
import { UmbDocumentItemRepository } from '@umbraco-cms/backoffice/document';
import { UmbMediaItemRepository } from '@umbraco-cms/backoffice/media';
import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository'; import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository';
import type { UmbUniqueItemModel } from '@umbraco-cms/backoffice/models'; import type { UmbUniqueItemModel } from '@umbraco-cms/backoffice/models';
import '../components/user-group-table-name-column-layout.element.js'; import '../components/user-group-table-name-column-layout.element.js';
import '../components/user-group-table-sections-column-layout.element.js'; import '../components/user-group-table-sections-column-layout.element.js';
import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry';
@customElement('umb-user-group-collection-table-view') @customElement('umb-user-group-collection-table-view')
export class UmbUserGroupCollectionTableViewElement extends UmbLitElement { export class UmbUserGroupCollectionTableViewElement extends UmbLitElement {
@@ -63,8 +62,8 @@ export class UmbUserGroupCollectionTableViewElement extends UmbLitElement {
#collectionContext?: UmbUserGroupCollectionContext; #collectionContext?: UmbUserGroupCollectionContext;
// TODO: hardcoded dependencies on document and media modules. We should figure out how these dependencies can be added through extensions. // TODO: hardcoded dependencies on document and media modules. We should figure out how these dependencies can be added through extensions.
#documentItemRepository = new UmbDocumentItemRepository(this); #documentItemRepository?: UmbItemRepository<UmbUniqueItemModel>;
#mediaItemRepository = new UmbMediaItemRepository(this); #mediaItemRepository?: UmbItemRepository<UmbUniqueItemModel>;
#documentStartNodeMap = new Map<string, string>(); #documentStartNodeMap = new Map<string, string>();
#mediaStartNodeMap = new Map<string, string>(); #mediaStartNodeMap = new Map<string, string>();
@@ -81,7 +80,8 @@ export class UmbUserGroupCollectionTableViewElement extends UmbLitElement {
); );
this.observe( this.observe(
this.#collectionContext.items, this.#collectionContext.items,
(items) => { async (items) => {
await this.#initRepositories();
this._createTableItems(items); this._createTableItems(items);
}, },
'umbCollectionItemsObserver', 'umbCollectionItemsObserver',
@@ -89,7 +89,29 @@ export class UmbUserGroupCollectionTableViewElement extends UmbLitElement {
}); });
} }
async #initRepositories() {
if (this.#documentItemRepository && this.#mediaItemRepository) return;
// TODO: get back to this when documents have been decoupled from users.
// The repository alias is hardcoded on purpose to avoid a document import in the user module.
this.#documentItemRepository = await createExtensionApiByAlias<UmbItemRepository<UmbUniqueItemModel>>(
this,
'Umb.Repository.DocumentItem',
);
// TODO: get back to this when media have been decoupled from users.
// The repository alias is hardcoded on purpose to avoid a media import in the user module.
this.#mediaItemRepository = await createExtensionApiByAlias<UmbItemRepository<UmbUniqueItemModel>>(
this,
'Umb.Repository.MediaItem',
);
}
private async _createTableItems(userGroups: Array<UmbUserGroupDetailModel>) { private async _createTableItems(userGroups: Array<UmbUserGroupDetailModel>) {
if (!this.#documentItemRepository || !this.#mediaItemRepository) {
throw new Error('Document and media item repositories are not initialized.');
}
await Promise.all([ await Promise.all([
this.#requestAndCacheStartNodes( this.#requestAndCacheStartNodes(
userGroups, userGroups,

View File

@@ -1,9 +1,8 @@
import { UUIRefNodeElement } from '@umbraco-cms/backoffice/external/uui'; import { UUIRefNodeElement } from '@umbraco-cms/backoffice/external/uui';
import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit'; import { css, customElement, html, property, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api'; import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { createExtensionApiByAlias, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { UmbDocumentItemRepository } from '@umbraco-cms/backoffice/document'; import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository';
import { UmbMediaItemRepository } from '@umbraco-cms/backoffice/media';
/** /**
* @element umb-user-group-ref * @element umb-user-group-ref
@@ -12,8 +11,8 @@ import { UmbMediaItemRepository } from '@umbraco-cms/backoffice/media';
*/ */
@customElement('umb-user-group-ref') @customElement('umb-user-group-ref')
export class UmbUserGroupRefElement extends UmbElementMixin(UUIRefNodeElement) { export class UmbUserGroupRefElement extends UmbElementMixin(UUIRefNodeElement) {
#documentItemRepository?: UmbDocumentItemRepository; #documentItemRepository?: UmbItemRepository<any>;
#mediaItemRepository?: UmbMediaItemRepository; #mediaItemRepository?: UmbItemRepository<any>;
@property({ type: Boolean }) @property({ type: Boolean })
documentRootAccess: boolean = false; documentRootAccess: boolean = false;
@@ -69,16 +68,21 @@ export class UmbUserGroupRefElement extends UmbElementMixin(UUIRefNodeElement) {
if (this.documentRootAccess) return; if (this.documentRootAccess) return;
if (!unique) return; if (!unique) return;
// TODO: get back to this when documents have been decoupled from users.
// The repository alias is hardcoded on purpose to avoid a document import in the user module.
if (!this.#documentItemRepository) { if (!this.#documentItemRepository) {
this.#documentItemRepository = new UmbDocumentItemRepository(this); this.#documentItemRepository = await createExtensionApiByAlias<UmbItemRepository<any>>(
this,
'Umb.Repository.DocumentItem',
);
} }
const { error, asObservable } = await this.#documentItemRepository.requestItems([unique]); const { error, asObservable } = await this.#documentItemRepository.requestItems([unique]);
if (error) return; if (error) return;
this.observe( this.observe(
asObservable(), asObservable?.(),
(data) => (this._documentLabel = data[0].variants?.[0].name ?? unique), (data) => (this._documentLabel = data?.[0].variants?.[0].name ?? unique),
'_observeDocumentStartNode', '_observeDocumentStartNode',
); );
} }
@@ -87,16 +91,21 @@ export class UmbUserGroupRefElement extends UmbElementMixin(UUIRefNodeElement) {
if (this.mediaRootAccess) return; if (this.mediaRootAccess) return;
if (!unique) return; if (!unique) return;
// TODO: get back to this when media have been decoupled from users.
// The repository alias is hardcoded on purpose to avoid a media import in the user module.
if (!this.#mediaItemRepository) { if (!this.#mediaItemRepository) {
this.#mediaItemRepository = new UmbMediaItemRepository(this); this.#mediaItemRepository = await createExtensionApiByAlias<UmbItemRepository<any>>(
this,
'Umb.Repository.MediaItem',
);
} }
const { error, asObservable } = await this.#mediaItemRepository.requestItems([unique]); const { error, asObservable } = await this.#mediaItemRepository.requestItems([unique]);
if (error) return; if (error) return;
this.observe( this.observe(
asObservable(), asObservable?.(),
(data) => (this._mediaLabel = data[0].variants?.[0].name ?? unique), (data) => (this._mediaLabel = data?.[0].variants?.[0].name ?? unique),
'_observeMediaStartNode', '_observeMediaStartNode',
); );
} }

View File

@@ -1,11 +1,11 @@
import { UmbUserGroupCollectionRepository } from '../../collection/repository/index.js'; import { UmbUserGroupCollectionRepository } from '../../collection/repository/index.js';
import type { UmbUserGroupDetailModel } from '../../types.js'; import type { UmbUserGroupDetailModel } from '../../types.js';
import type { UMB_USER_GROUP_PICKER_MODAL } from './user-group-picker-modal.token.js';
import { css, customElement, html, repeat, state, when } from '@umbraco-cms/backoffice/external/lit'; import { css, customElement, html, repeat, state, when } from '@umbraco-cms/backoffice/external/lit';
import { debounce, UmbSelectionManager } from '@umbraco-cms/backoffice/utils'; import { debounce, UmbSelectionManager } from '@umbraco-cms/backoffice/utils';
import { umbFocus } from '@umbraco-cms/backoffice/lit-element'; import { umbFocus } from '@umbraco-cms/backoffice/lit-element';
import { UmbDeselectedEvent, UmbSelectedEvent } from '@umbraco-cms/backoffice/event'; import { UmbDeselectedEvent, UmbSelectedEvent } from '@umbraco-cms/backoffice/event';
import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal';
import type { UMB_USER_GROUP_PICKER_MODAL } from '@umbraco-cms/backoffice/user-group';
import type { UUIInputEvent, UUIMenuItemEvent } from '@umbraco-cms/backoffice/external/uui'; import type { UUIInputEvent, UUIMenuItemEvent } from '@umbraco-cms/backoffice/external/uui';
import '../../components/user-group-ref/user-group-ref.element.js'; import '../../components/user-group-ref/user-group-ref.element.js';

View File

@@ -5,10 +5,8 @@ import type { UUIBooleanInputEvent } from '@umbraco-cms/backoffice/external/uui'
import { css, html, nothing, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { css, html, nothing, customElement, state, ifDefined } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement, umbFocus } from '@umbraco-cms/backoffice/lit-element'; import { UmbLitElement, umbFocus } from '@umbraco-cms/backoffice/lit-element';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import type { UmbInputDocumentElement } from '@umbraco-cms/backoffice/document';
import type { UmbInputSectionElement } from '@umbraco-cms/backoffice/section'; import type { UmbInputSectionElement } from '@umbraco-cms/backoffice/section';
import type { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import type { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
import type { UmbInputMediaElement } from '@umbraco-cms/backoffice/media';
import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { umbOpenModal } from '@umbraco-cms/backoffice/modal';
import type { UmbInputLanguageElement } from '@umbraco-cms/backoffice/language'; import type { UmbInputLanguageElement } from '@umbraco-cms/backoffice/language';
import { UMB_ICON_PICKER_MODAL } from '@umbraco-cms/backoffice/icon'; import { UMB_ICON_PICKER_MODAL } from '@umbraco-cms/backoffice/icon';
@@ -145,7 +143,9 @@ export class UmbUserGroupWorkspaceEditorElement extends UmbLitElement {
#onDocumentStartNodeChange(event: CustomEvent) { #onDocumentStartNodeChange(event: CustomEvent) {
event.stopPropagation(); event.stopPropagation();
const target = event.target as UmbInputDocumentElement; // TODO: get back to this when documents have been decoupled from users.
// The event target is deliberately set to any to avoid an import cycle with documents.
const target = event.target as any;
const selected = target.selection?.[0]; const selected = target.selection?.[0];
// TODO make contexts method // TODO make contexts method
this.#workspaceContext?.updateProperty('documentStartNode', selected ? { unique: selected } : null); this.#workspaceContext?.updateProperty('documentStartNode', selected ? { unique: selected } : null);
@@ -161,7 +161,9 @@ export class UmbUserGroupWorkspaceEditorElement extends UmbLitElement {
#onMediaStartNodeChange(event: CustomEvent) { #onMediaStartNodeChange(event: CustomEvent) {
event.stopPropagation(); event.stopPropagation();
const target = event.target as UmbInputMediaElement; // TODO: get back to this when media have been decoupled from users.
// The event target is deliberately set to any to avoid an import cycle with media.
const target = event.target as any;
const selected = target.selection?.[0]; const selected = target.selection?.[0];
// TODO make contexts method // TODO make contexts method
this.#workspaceContext?.updateProperty('mediaStartNode', selected ? { unique: selected } : null); this.#workspaceContext?.updateProperty('mediaStartNode', selected ? { unique: selected } : null);

View File

@@ -1,7 +1,7 @@
import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry';
import { html, customElement, property, repeat, state } from '@umbraco-cms/backoffice/external/lit'; import { html, customElement, property, repeat, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { UmbDocumentItemModel } from '@umbraco-cms/backoffice/document'; import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository';
import { UmbDocumentItemRepository } from '@umbraco-cms/backoffice/document';
@customElement('umb-user-document-start-node') @customElement('umb-user-document-start-node')
export class UmbUserDocumentStartNodeElement extends UmbLitElement { export class UmbUserDocumentStartNodeElement extends UmbLitElement {
@@ -22,14 +22,15 @@ export class UmbUserDocumentStartNodeElement extends UmbLitElement {
readonly = false; readonly = false;
@state() @state()
_displayValue: Array<UmbDocumentItemModel> = []; _displayValue: Array<any> = [];
#itemRepository = new UmbDocumentItemRepository(this);
async #observeItems() { async #observeItems() {
const { asObservable } = await this.#itemRepository.requestItems(this.#uniques); // TODO: get back to this when documents have been decoupled from users.
// The repository alias is hardcoded on purpose to avoid a document import in the user module.
const itemRepository = await createExtensionApiByAlias<UmbItemRepository<any>>(this, 'Umb.Repository.DocumentItem');
const { asObservable } = await itemRepository.requestItems(this.#uniques);
this.observe(asObservable(), (data) => { this.observe(asObservable?.(), (data) => {
this._displayValue = data || []; this._displayValue = data || [];
}); });
} }

View File

@@ -1,7 +1,7 @@
import { createExtensionApiByAlias } from '@umbraco-cms/backoffice/extension-registry';
import { html, customElement, property, repeat, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { html, customElement, property, repeat, state, ifDefined } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { UmbMediaItemModel } from '@umbraco-cms/backoffice/media'; import type { UmbItemRepository } from '@umbraco-cms/backoffice/repository';
import { UmbMediaItemRepository } from '@umbraco-cms/backoffice/media';
@customElement('umb-user-media-start-node') @customElement('umb-user-media-start-node')
export class UmbUserMediaStartNodeElement extends UmbLitElement { export class UmbUserMediaStartNodeElement extends UmbLitElement {
@@ -22,14 +22,15 @@ export class UmbUserMediaStartNodeElement extends UmbLitElement {
readonly = false; readonly = false;
@state() @state()
_displayValue: Array<UmbMediaItemModel> = []; _displayValue: Array<any> = [];
#itemRepository = new UmbMediaItemRepository(this);
async #observeItems() { async #observeItems() {
const { asObservable } = await this.#itemRepository.requestItems(this.#uniques); // TODO: get back to this when documents have been decoupled from users.
// The repository alias is hardcoded on purpose to avoid a document import in the user module.
const itemRepository = await createExtensionApiByAlias<UmbItemRepository<any>>(this, 'Umb.Repository.MediaItem');
const { asObservable } = await itemRepository.requestItems(this.#uniques);
this.observe(asObservable(), (data) => { this.observe(asObservable?.(), (data) => {
this._displayValue = data || []; this._displayValue = data || [];
}); });
} }

View File

@@ -1,4 +1,4 @@
import type { UmbUserDetailModel } from '@umbraco-cms/backoffice/user'; import type { UmbUserDetailModel } from '../../types.js';
import type { UmbPickerModalData } from '@umbraco-cms/backoffice/modal'; import type { UmbPickerModalData } from '@umbraco-cms/backoffice/modal';
import { UmbModalToken } from '@umbraco-cms/backoffice/modal'; import { UmbModalToken } from '@umbraco-cms/backoffice/modal';

View File

@@ -1,3 +1,4 @@
import type { UmbUserInputElement } from '../../components/index.js';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
@@ -5,7 +6,6 @@ import type {
UmbPropertyEditorConfigCollection, UmbPropertyEditorConfigCollection,
UmbPropertyEditorUiElement, UmbPropertyEditorUiElement,
} from '@umbraco-cms/backoffice/property-editor'; } from '@umbraco-cms/backoffice/property-editor';
import type { UmbUserInputElement } from '@umbraco-cms/backoffice/user';
/** /**
* @element umb-property-editor-ui-user-picker * @element umb-property-editor-ui-user-picker

View File

@@ -3,8 +3,6 @@ import type { UmbUserDetailModel } from '../../../../types.js';
import { html, customElement, state, nothing, css } from '@umbraco-cms/backoffice/external/lit'; import { html, customElement, state, nothing, css } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import type { UmbInputDocumentElement } from '@umbraco-cms/backoffice/document';
import type { UmbInputMediaElement } from '@umbraco-cms/backoffice/media';
import type { UmbUserGroupInputElement } from '@umbraco-cms/backoffice/user-group'; import type { UmbUserGroupInputElement } from '@umbraco-cms/backoffice/user-group';
import type { UUIBooleanInputEvent } from '@umbraco-cms/backoffice/external/uui'; import type { UUIBooleanInputEvent } from '@umbraco-cms/backoffice/external/uui';
import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models'; import type { UmbReferenceByUnique } from '@umbraco-cms/backoffice/models';
@@ -87,8 +85,10 @@ export class UmbUserWorkspaceAssignAccessElement extends UmbLitElement {
#onDocumentStartNodeChange(event: CustomEvent) { #onDocumentStartNodeChange(event: CustomEvent) {
event.stopPropagation(); event.stopPropagation();
const target = event.target as UmbInputDocumentElement; // TODO: get back to this when media have been decoupled from users.
const selection: Array<UmbReferenceByUnique> = target.selection.map((unique) => { // The event target is deliberately set to any to avoid an import cycle with media.
const target = event.target as any;
const selection: Array<UmbReferenceByUnique> = target.selection.map((unique: string) => {
return { unique }; return { unique };
}); });
// TODO make contexts method // TODO make contexts method
@@ -105,8 +105,10 @@ export class UmbUserWorkspaceAssignAccessElement extends UmbLitElement {
#onMediaStartNodeChange(event: CustomEvent) { #onMediaStartNodeChange(event: CustomEvent) {
event.stopPropagation(); event.stopPropagation();
const target = event.target as UmbInputMediaElement; // TODO: get back to this when media have been decoupled from users.
const selection: Array<UmbReferenceByUnique> = target.selection.map((unique) => { // The event target is deliberately set to any to avoid an import cycle with media.
const target = event.target as any;
const selection: Array<UmbReferenceByUnique> = target.selection.map((unique: string) => {
return { unique }; return { unique };
}); });
// TODO make contexts method // TODO make contexts method