diff --git a/core/src/components/accordion-group/accordion-group.tsx b/core/src/components/accordion-group/accordion-group.tsx index b84d75b67a5..ba5b110c8b6 100644 --- a/core/src/components/accordion-group/accordion-group.tsx +++ b/core/src/components/accordion-group/accordion-group.tsx @@ -87,7 +87,7 @@ export class AccordionGroup implements ComponentInterface { * Custom behavior: ['a', 'b'] */ printIonWarning( - `ion-accordion-group was passed an array of values, but multiple="false". This is incorrect usage and may result in unexpected behaviors. To dismiss this warning, pass a string to the "value" property when multiple="false". + `[ion-accordion-group] - An array of values was passed, but multiple is "false". This is incorrect usage and may result in unexpected behaviors. To dismiss this warning, pass a string to the "value" property when multiple="false". Value Passed: [${value.map((v) => `'${v}'`).join(', ')}] `, diff --git a/core/src/components/alert/alert.tsx b/core/src/components/alert/alert.tsx index dc598fbdf0a..c126d9b0d2a 100644 --- a/core/src/components/alert/alert.tsx +++ b/core/src/components/alert/alert.tsx @@ -5,6 +5,7 @@ import type { Gesture } from '@utils/gesture'; import { createButtonActiveGesture } from '@utils/gesture/button-active'; import { raf } from '@utils/helpers'; import { createLockController } from '@utils/lock-controller'; +import { printIonWarning } from '@utils/logging'; import { createDelegateController, createTriggerController, @@ -318,8 +319,8 @@ export class Alert implements ComponentInterface, OverlayInterface { // checkboxes and inputs are all accepted, but they cannot be mixed. const inputTypes = new Set(inputs.map((i) => i.type)); if (inputTypes.has('checkbox') && inputTypes.has('radio')) { - console.warn( - `Alert cannot mix input types: ${Array.from(inputTypes.values()).join( + printIonWarning( + `[ion-alert] - Alert cannot mix input types: ${Array.from(inputTypes.values()).join( '/' )}. Please see alert docs for more info.` ); diff --git a/core/src/components/app/app.tsx b/core/src/components/app/app.tsx index fadfed3f04b..4f4919667f4 100644 --- a/core/src/components/app/app.tsx +++ b/core/src/components/app/app.tsx @@ -46,7 +46,7 @@ export class App implements ComponentInterface { */ if (shouldUseCloseWatcher()) { printIonWarning( - 'experimentalCloseWatcher was set to `true`, but hardwareBackButton was set to `false`. Both config options must be `true` for the Close Watcher API to be used.' + '[ion-app] - experimentalCloseWatcher was set to `true`, but hardwareBackButton was set to `false`. Both config options must be `true` for the Close Watcher API to be used.' ); } diff --git a/core/src/components/button/button.tsx b/core/src/components/button/button.tsx index b3dda48877e..4e63b8413ea 100644 --- a/core/src/components/button/button.tsx +++ b/core/src/components/button/button.tsx @@ -235,7 +235,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf * element with that id is not a form element. */ printIonWarning( - `Form with selector: "#${form}" could not be found. Verify that the id is attached to a
element.`, + `[ion-button] - Form with selector: "#${form}" could not be found. Verify that the id is attached to a element.`, this.el ); return null; @@ -246,7 +246,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf * element with that id could not be found in the DOM. */ printIonWarning( - `Form with selector: "#${form}" could not be found. Verify that the id is correct and the form is rendered in the DOM.`, + `[ion-button] - Form with selector: "#${form}" could not be found. Verify that the id is correct and the form is rendered in the DOM.`, this.el ); return null; @@ -260,7 +260,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf * as the form attribute. */ printIonWarning( - `The provided "form" element is invalid. Verify that the form is a HTMLFormElement and rendered in the DOM.`, + `[ion-button] - The provided "form" element is invalid. Verify that the form is a HTMLFormElement and rendered in the DOM.`, this.el ); return null; diff --git a/core/src/components/button/test/form-reference/button.e2e.ts b/core/src/components/button/test/form-reference/button.e2e.ts index 0686b112982..5710f52da67 100644 --- a/core/src/components/button/test/form-reference/button.e2e.ts +++ b/core/src/components/button/test/form-reference/button.e2e.ts @@ -169,7 +169,7 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) => expect(logs.length).toBe(1); expect(logs[0]).toContain( - '[Ionic Warning]: Form with selector: "#missingForm" could not be found. Verify that the id is correct and the form is rendered in the DOM.' + '[Ionic Warning]: [ion-button] - Form with selector: "#missingForm" could not be found. Verify that the id is correct and the form is rendered in the DOM.' ); }); @@ -197,7 +197,7 @@ configs({ directions: ['ltr'], modes: ['ios'] }).forEach(({ title, config }) => expect(logs.length).toBe(1); expect(logs[0]).toContain( - '[Ionic Warning]: The provided "form" element is invalid. Verify that the form is a HTMLFormElement and rendered in the DOM.' + '[Ionic Warning]: [ion-button] - The provided "form" element is invalid. Verify that the form is a HTMLFormElement and rendered in the DOM.' ); }); }); diff --git a/core/src/components/datetime-button/datetime-button.tsx b/core/src/components/datetime-button/datetime-button.tsx index 957a2267f09..af0a77f88e0 100644 --- a/core/src/components/datetime-button/datetime-button.tsx +++ b/core/src/components/datetime-button/datetime-button.tsx @@ -63,7 +63,7 @@ export class DatetimeButton implements ComponentInterface { const { datetime } = this; if (!datetime) { printIonError( - 'An ID associated with an ion-datetime instance is required for ion-datetime-button to function properly.', + '[ion-datetime-button] - An ID associated with an ion-datetime instance is required to function properly.', this.el ); return; @@ -71,7 +71,7 @@ export class DatetimeButton implements ComponentInterface { const datetimeEl = (this.datetimeEl = document.getElementById(datetime) as HTMLIonDatetimeElement | null); if (!datetimeEl) { - printIonError(`No ion-datetime instance found for ID '${datetime}'.`, this.el); + printIonError(`[ion-datetime-button] - No ion-datetime instance found for ID '${datetime}'.`, this.el); return; } @@ -81,7 +81,7 @@ export class DatetimeButton implements ComponentInterface { */ if (datetimeEl.tagName !== 'ION-DATETIME') { printIonError( - `Expected an ion-datetime instance for ID '${datetime}' but received '${datetimeEl.tagName.toLowerCase()}' instead.`, + `[ion-datetime-button] - Expected an ion-datetime instance for ID '${datetime}' but received '${datetimeEl.tagName.toLowerCase()}' instead.`, datetimeEl ); return; @@ -245,7 +245,7 @@ export class DatetimeButton implements ComponentInterface { try { headerText = titleSelectedDatesFormatter(parsedValues); } catch (e) { - printIonError('Exception in provided `titleSelectedDatesFormatter`: ', e); + printIonError('[ion-datetime-button] - Exception in provided `titleSelectedDatesFormatter`:', e); } } this.dateText = headerText; diff --git a/core/src/components/datetime/datetime.tsx b/core/src/components/datetime/datetime.tsx index fcdb8bcabe0..b63590c66bb 100644 --- a/core/src/components/datetime/datetime.tsx +++ b/core/src/components/datetime/datetime.tsx @@ -584,7 +584,7 @@ export class Datetime implements ComponentInterface { * Custom behavior: ['a', 'b'] */ printIonWarning( - `ion-datetime was passed an array of values, but multiple="false". This is incorrect usage and may result in unexpected behaviors. To dismiss this warning, pass a string to the "value" property when multiple="false". + `[ion-datetime] - An array of values was passed, but multiple is "false". This is incorrect usage and may result in unexpected behaviors. To dismiss this warning, pass a string to the "value" property when multiple="false". Value Passed: [${value.map((v) => `'${v}'`).join(', ')}] `, @@ -1389,24 +1389,24 @@ export class Datetime implements ComponentInterface { if (multiple) { if (presentation !== 'date') { - printIonWarning('Multiple date selection is only supported for presentation="date".', el); + printIonWarning('[ion-datetime] - Multiple date selection is only supported for presentation="date".', el); } if (preferWheel) { - printIonWarning('Multiple date selection is not supported with preferWheel="true".', el); + printIonWarning('[ion-datetime] - Multiple date selection is not supported with preferWheel="true".', el); } } if (highlightedDates !== undefined) { if (presentation !== 'date' && presentation !== 'date-time' && presentation !== 'time-date') { printIonWarning( - 'The highlightedDates property is only supported with the date, date-time, and time-date presentations.', + '[ion-datetime] - The highlightedDates property is only supported with the date, date-time, and time-date presentations.', el ); } if (preferWheel) { - printIonWarning('The highlightedDates property is not supported with preferWheel="true".', el); + printIonWarning('[ion-datetime] - The highlightedDates property is not supported with preferWheel="true".', el); } } @@ -1668,7 +1668,7 @@ export class Datetime implements ComponentInterface { disabled = !isDateEnabled(convertDataToISO(referenceParts)); } catch (e) { printIonError( - 'Exception thrown from provided `isDateEnabled` function. Please check your function and try again.', + '[ion-datetime] - Exception thrown from provided `isDateEnabled` function. Please check your function and try again.', e ); } @@ -1759,7 +1759,7 @@ export class Datetime implements ComponentInterface { disabled = !isDateEnabled(convertDataToISO(referenceParts)); } catch (e) { printIonError( - 'Exception thrown from provided `isDateEnabled` function. Please check your function and try again.', + '[ion-datetime] - Exception thrown from provided `isDateEnabled` function. Please check your function and try again.', e ); } @@ -2262,7 +2262,7 @@ export class Datetime implements ComponentInterface { isCalDayDisabled = !isDateEnabled(dateIsoString); } catch (e) { printIonError( - 'Exception thrown from provided `isDateEnabled` function. Please check your function and try again.', + '[ion-datetime] - Exception thrown from provided `isDateEnabled` function. Please check your function and try again.', el, e ); @@ -2483,7 +2483,7 @@ export class Datetime implements ComponentInterface { try { headerText = titleSelectedDatesFormatter(convertDataToISO(activeParts)); } catch (e) { - printIonError('Exception in provided `titleSelectedDatesFormatter`: ', e); + printIonError('[ion-datetime] - Exception in provided `titleSelectedDatesFormatter`:', e); } } } else { diff --git a/core/src/components/datetime/test/basic/datetime.e2e.ts b/core/src/components/datetime/test/basic/datetime.e2e.ts index f96cc64bea9..67812b6d020 100644 --- a/core/src/components/datetime/test/basic/datetime.e2e.ts +++ b/core/src/components/datetime/test/basic/datetime.e2e.ts @@ -687,7 +687,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { expect(logs.length).toBe(1); expect(logs[0]).toContain( - '[Ionic Warning]: Datetime: "timeZone" and "timeZoneName" are not supported in "formatOptions".' + '[Ionic Warning]: [ion-datetime] - "timeZone" and "timeZoneName" are not supported in "formatOptions".' ); }); @@ -717,7 +717,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { expect(logs.length).toBe(1); expect(logs[0]).toContain( - "[Ionic Warning]: Datetime: The 'date-time' presentation requires either a date or time object (or both) in formatOptions." + "[Ionic Warning]: [ion-datetime] - The 'date-time' presentation requires either a date or time object (or both) in formatOptions." ); }); }); diff --git a/core/src/components/datetime/utils/comparison.ts b/core/src/components/datetime/utils/comparison.ts index 0507b16908a..986d5839222 100644 --- a/core/src/components/datetime/utils/comparison.ts +++ b/core/src/components/datetime/utils/comparison.ts @@ -48,7 +48,7 @@ export const warnIfValueOutOfBounds = ( for (const val of valueArray) { if ((min !== undefined && isBefore(val, min)) || (max !== undefined && isAfter(val, max))) { printIonWarning( - 'The value provided to ion-datetime is out of bounds.\n\n' + + '[ion-datetime] - The value provided to ion-datetime is out of bounds.\n\n' + `Min: ${JSON.stringify(min)}\n` + `Max: ${JSON.stringify(max)}\n` + `Value: ${JSON.stringify(value)}` diff --git a/core/src/components/datetime/utils/parse.ts b/core/src/components/datetime/utils/parse.ts index efda69f655b..ef18892b233 100644 --- a/core/src/components/datetime/utils/parse.ts +++ b/core/src/components/datetime/utils/parse.ts @@ -105,7 +105,9 @@ export function parseDate(val: string | string[] | undefined | null): DatetimePa if (parse === null) { // wasn't able to parse the ISO datetime - printIonWarning(`Unable to parse date string: ${val}. Please provide a valid ISO 8601 datetime string.`); + printIonWarning( + `[ion-datetime] - Unable to parse date string: ${val}. Please provide a valid ISO 8601 datetime string.` + ); return undefined; } diff --git a/core/src/components/datetime/utils/state.ts b/core/src/components/datetime/utils/state.ts index f709d7289a1..80abeb25f53 100644 --- a/core/src/components/datetime/utils/state.ts +++ b/core/src/components/datetime/utils/state.ts @@ -218,7 +218,7 @@ export const getHighlightStyles = ( return highlightedDates(dateIsoString); } catch (e) { printIonError( - 'Exception thrown from provided `highlightedDates` callback. Please check your function and try again.', + '[ion-datetime] - Exception thrown from provided `highlightedDates` callback. Please check your function and try again.', el, e ); diff --git a/core/src/components/datetime/utils/validate.ts b/core/src/components/datetime/utils/validate.ts index 46c3fe633b8..d11ceeec722 100644 --- a/core/src/components/datetime/utils/validate.ts +++ b/core/src/components/datetime/utils/validate.ts @@ -14,7 +14,7 @@ export const warnIfTimeZoneProvided = (el: HTMLElement, formatOptions?: FormatOp formatOptions?.time?.timeZone || formatOptions?.time?.timeZoneName ) { - printIonWarning('Datetime: "timeZone" and "timeZoneName" are not supported in "formatOptions".', el); + printIonWarning('[ion-datetime] - "timeZone" and "timeZoneName" are not supported in "formatOptions".', el); } }; @@ -33,19 +33,22 @@ export const checkForPresentationFormatMismatch = ( case 'month': case 'year': if (formatOptions.date === undefined) { - printIonWarning(`Datetime: The '${presentation}' presentation requires a date object in formatOptions.`, el); + printIonWarning( + `[ion-datetime] - The '${presentation}' presentation requires a date object in formatOptions.`, + el + ); } break; case 'time': if (formatOptions.time === undefined) { - printIonWarning(`Datetime: The 'time' presentation requires a time object in formatOptions.`, el); + printIonWarning(`[ion-datetime] - The 'time' presentation requires a time object in formatOptions.`, el); } break; case 'date-time': case 'time-date': if (formatOptions.date === undefined && formatOptions.time === undefined) { printIonWarning( - `Datetime: The '${presentation}' presentation requires either a date or time object (or both) in formatOptions.`, + `[ion-datetime] - The '${presentation}' presentation requires either a date or time object (or both) in formatOptions.`, el ); } diff --git a/core/src/components/input-password-toggle/input-password-toggle.tsx b/core/src/components/input-password-toggle/input-password-toggle.tsx index a783cfc9af3..44b8e0828a1 100644 --- a/core/src/components/input-password-toggle/input-password-toggle.tsx +++ b/core/src/components/input-password-toggle/input-password-toggle.tsx @@ -59,7 +59,7 @@ export class InputPasswordToggle implements ComponentInterface { onTypeChange(newValue: TextFieldTypes) { if (newValue !== 'text' && newValue !== 'password') { printIonWarning( - `ion-input-password-toggle only supports inputs of type "text" or "password". Input of type "${newValue}" is not compatible.`, + `[ion-input-password-toggle] - Only inputs of type "text" or "password" are supported. Input of type "${newValue}" is not compatible.`, this.el ); @@ -74,7 +74,7 @@ export class InputPasswordToggle implements ComponentInterface { if (!inputElRef) { printIonWarning( - 'No ancestor ion-input found for ion-input-password-toggle. This component must be slotted inside of an ion-input.', + '[ion-input-password-toggle] - No ancestor ion-input found. This component must be slotted inside of an ion-input.', el ); diff --git a/core/src/components/input/input.utils.ts b/core/src/components/input/input.utils.ts index d6812be12d8..1567a3b52cd 100644 --- a/core/src/components/input/input.utils.ts +++ b/core/src/components/input/input.utils.ts @@ -24,7 +24,7 @@ export const getCounterText = ( try { return counterFormatter(valueLength, maxLength); } catch (e) { - printIonError('Exception in provided `counterFormatter`.', e); + printIonError('[ion-input] - Exception in provided `counterFormatter`:', e); return defaultCounterText; } }; diff --git a/core/src/components/item-sliding/item-sliding.tsx b/core/src/components/item-sliding/item-sliding.tsx index a250b5e9bfe..ef9eaae327c 100644 --- a/core/src/components/item-sliding/item-sliding.tsx +++ b/core/src/components/item-sliding/item-sliding.tsx @@ -2,6 +2,7 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core'; import { Component, Element, Event, Host, Method, Prop, State, Watch, h } from '@stencil/core'; import { findClosestIonContent, disableContentScrollY, resetContentScrollY } from '@utils/content'; import { isEndSide } from '@utils/helpers'; +import { printIonWarning } from '@utils/logging'; import { watchForOptions } from '@utils/watch-options'; import { getIonMode } from '../../global/ionic-global'; @@ -343,7 +344,7 @@ export class ItemSliding implements ComponentInterface { case ItemSide.None: return; default: - console.warn('invalid ItemSideFlags value', this.sides); + printIonWarning('[ion-item-sliding] - invalid ItemSideFlags value', this.sides); break; } diff --git a/core/src/components/menu/menu.tsx b/core/src/components/menu/menu.tsx index 8a70dd8fb1d..0fee7bda169 100644 --- a/core/src/components/menu/menu.tsx +++ b/core/src/components/menu/menu.tsx @@ -6,6 +6,7 @@ import { GESTURE_CONTROLLER } from '@utils/gesture'; import { shouldUseCloseWatcher } from '@utils/hardware-back-button'; import type { Attributes } from '@utils/helpers'; import { inheritAriaAttributes, assert, clamp, isEndSide as isEnd } from '@utils/helpers'; +import { printIonError } from '@utils/logging'; import { menuController } from '@utils/menu-controller'; import { BACKDROP, GESTURE, getPresentedOverlay } from '@utils/overlays'; import { isPlatform } from '@utils/platform'; @@ -215,13 +216,13 @@ export class Menu implements ComponentInterface, MenuI { const content = this.contentId !== undefined ? document.getElementById(this.contentId) : null; if (content === null) { - console.error('Menu: must have a "content" element to listen for drag events on.'); + printIonError('[ion-menu] - Must have a "content" element to listen for drag events on.'); return; } if (this.el.contains(content)) { - console.error( - `Menu: "contentId" should refer to the main view's ion-content, not the ion-content inside of the ion-menu.` + printIonError( + `[ion-menu] - The "contentId" should refer to the main view's ion-content, not the ion-content inside of the ion-menu.` ); } diff --git a/core/src/components/modal/modal.tsx b/core/src/components/modal/modal.tsx index c6f15bdce83..2ec55fca020 100644 --- a/core/src/components/modal/modal.tsx +++ b/core/src/components/modal/modal.tsx @@ -427,7 +427,7 @@ export class Modal implements ComponentInterface, OverlayInterface { } if (breakpoints !== undefined && initialBreakpoint !== undefined && !breakpoints.includes(initialBreakpoint)) { - printIonWarning('Your breakpoints array must include the initialBreakpoint value.'); + printIonWarning('[ion-modal] - Your breakpoints array must include the initialBreakpoint value.'); } if (!this.htmlAttributes?.id) { @@ -847,12 +847,12 @@ export class Modal implements ComponentInterface, OverlayInterface { @Method() async setCurrentBreakpoint(breakpoint: number): Promise { if (!this.isSheetModal) { - printIonWarning('setCurrentBreakpoint is only supported on sheet modals.'); + printIonWarning('[ion-modal] - setCurrentBreakpoint is only supported on sheet modals.'); return; } if (!this.breakpoints!.includes(breakpoint)) { printIonWarning( - `Attempted to set invalid breakpoint value ${breakpoint}. Please double check that the breakpoint value is part of your defined breakpoints.` + `[ion-modal] - Attempted to set invalid breakpoint value ${breakpoint}. Please double check that the breakpoint value is part of your defined breakpoints.` ); return; } diff --git a/core/src/components/modal/test/basic/modal.e2e.ts b/core/src/components/modal/test/basic/modal.e2e.ts index f431b6fe46f..325c4b3fbb5 100644 --- a/core/src/components/modal/test/basic/modal.e2e.ts +++ b/core/src/components/modal/test/basic/modal.e2e.ts @@ -145,7 +145,9 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => await modal.evaluate((el: HTMLIonModalElement) => el.setCurrentBreakpoint(0.5)); expect(warnings.length).toBe(1); - expect(warnings[0]).toBe('[Ionic Warning]: setCurrentBreakpoint is only supported on sheet modals.'); + expect(warnings[0]).toBe( + '[Ionic Warning]: [ion-modal] - setCurrentBreakpoint is only supported on sheet modals.' + ); }); test('it should return undefined when getting the breakpoint on a non-sheet modal', async ({ page }) => { diff --git a/core/src/components/modal/test/sheet/modal.e2e.ts b/core/src/components/modal/test/sheet/modal.e2e.ts index 6afb4cd67e1..469040602fb 100644 --- a/core/src/components/modal/test/sheet/modal.e2e.ts +++ b/core/src/components/modal/test/sheet/modal.e2e.ts @@ -96,7 +96,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => test('it should warn when setting an invalid breakpoint', async () => { expect(warnings.length).toBe(1); expect(warnings[0]).toBe( - '[Ionic Warning]: Attempted to set invalid breakpoint value 0.01. Please double check that the breakpoint value is part of your defined breakpoints.' + '[Ionic Warning]: [ion-modal] - Attempted to set invalid breakpoint value 0.01. Please double check that the breakpoint value is part of your defined breakpoints.' ); }); }); diff --git a/core/src/components/nav/nav.tsx b/core/src/components/nav/nav.tsx index cc71af4f6c5..3ecd39c2aa6 100644 --- a/core/src/components/nav/nav.tsx +++ b/core/src/components/nav/nav.tsx @@ -97,7 +97,7 @@ export class Nav implements NavOutlet { this.setRoot(this.root, this.rootParams); } } else if (isDev) { - printIonWarning(' does not support a root attribute when using ion-router.', this.el); + printIonWarning('[ion-nav] - A root attribute is not supported when using ion-router.', this.el); } } @@ -820,8 +820,8 @@ export class Nav implements NavOutlet { const finalNumViews = this.views.length + (insertViews?.length ?? 0) - (removeCount ?? 0); assert(finalNumViews >= 0, 'final balance can not be negative'); if (finalNumViews === 0) { - console.warn( - `You can't remove all the pages in the navigation stack. nav.pop() is probably called too many times.`, + printIonWarning( + `[ion-nav] - You can't remove all the pages in the navigation stack. nav.pop() is probably called too many times.`, this, this.el ); diff --git a/core/src/components/picker-legacy/picker.tsx b/core/src/components/picker-legacy/picker.tsx index 6b267823928..fe67cf83fec 100644 --- a/core/src/components/picker-legacy/picker.tsx +++ b/core/src/components/picker-legacy/picker.tsx @@ -206,7 +206,7 @@ export class Picker implements ComponentInterface, OverlayInterface { componentDidLoad() { printIonWarning( - 'ion-picker-legacy and ion-picker-legacy-column have been deprecated in favor of new versions of the ion-picker and ion-picker-column components. These new components display inline with your page content allowing for more presentation flexibility than before.', + '[ion-picker-legacy] - ion-picker-legacy and ion-picker-legacy-column have been deprecated in favor of new versions of the ion-picker and ion-picker-column components. These new components display inline with your page content allowing for more presentation flexibility than before.', this.el ); diff --git a/core/src/components/popover/popover.tsx b/core/src/components/popover/popover.tsx index d1205ab2f6a..61d525dff9a 100644 --- a/core/src/components/popover/popover.tsx +++ b/core/src/components/popover/popover.tsx @@ -648,7 +648,7 @@ export class Popover implements ComponentInterface, PopoverInterface { const triggerEl = (this.triggerEl = trigger !== undefined ? document.getElementById(trigger) : null); if (!triggerEl) { printIonWarning( - `A trigger element with the ID "${trigger}" was not found in the DOM. The trigger element must be in the DOM when the "trigger" property is set on ion-popover.`, + `[ion-popover] - A trigger element with the ID "${trigger}" was not found in the DOM. The trigger element must be in the DOM when the "trigger" property is set on ion-popover.`, this.el ); return; diff --git a/core/src/components/range/range.tsx b/core/src/components/range/range.tsx index 478b2a57e2e..eac3dd3ebcd 100644 --- a/core/src/components/range/range.tsx +++ b/core/src/components/range/range.tsx @@ -183,13 +183,13 @@ export class Range implements ComponentInterface { if (activeBarStart !== undefined) { if (activeBarStart > this.max) { printIonWarning( - `Range: The value of activeBarStart (${activeBarStart}) is greater than the max (${this.max}). Valid values are greater than or equal to the min value and less than or equal to the max value.`, + `[ion-range] - The value of activeBarStart (${activeBarStart}) is greater than the max (${this.max}). Valid values are greater than or equal to the min value and less than or equal to the max value.`, this.el ); this.activeBarStart = this.max; } else if (activeBarStart < this.min) { printIonWarning( - `Range: The value of activeBarStart (${activeBarStart}) is less than the min (${this.min}). Valid values are greater than or equal to the min value and less than or equal to the max value.`, + `[ion-range] - The value of activeBarStart (${activeBarStart}) is less than the min (${this.min}). Valid values are greater than or equal to the min value and less than or equal to the max value.`, this.el ); this.activeBarStart = this.min; diff --git a/core/src/components/refresher/refresher.tsx b/core/src/components/refresher/refresher.tsx index 25a121a3d0c..8d016873086 100644 --- a/core/src/components/refresher/refresher.tsx +++ b/core/src/components/refresher/refresher.tsx @@ -8,6 +8,7 @@ import { printIonContentErrorMsg, } from '@utils/content'; import { clamp, componentOnReady, getElementRoot, raf, transitionEndAsync } from '@utils/helpers'; +import { printIonError } from '@utils/logging'; import { ImpactStyle, hapticImpact } from '@utils/native/haptic'; import { getIonMode } from '../../global/ionic-global'; @@ -452,7 +453,7 @@ export class Refresher implements ComponentInterface { async connectedCallback() { if (this.el.getAttribute('slot') !== 'fixed') { - console.error('Make sure you use: '); + printIonError('[ion-refresher] - Make sure you use: '); return; } const contentEl = this.el.closest(ION_CONTENT_ELEMENT_SELECTOR); diff --git a/core/src/components/router-outlet/router-outlet.tsx b/core/src/components/router-outlet/router-outlet.tsx index d25234f814e..5187bddb6ed 100644 --- a/core/src/components/router-outlet/router-outlet.tsx +++ b/core/src/components/router-outlet/router-outlet.tsx @@ -4,6 +4,7 @@ import { getTimeGivenProgression } from '@utils/animation/cubic-bezier'; import { attachComponent, detachComponent } from '@utils/framework-delegate'; import { shallowEqualStringMap, hasLazyBuild } from '@utils/helpers'; import { createLockController } from '@utils/lock-controller'; +import { printIonError } from '@utils/logging'; import { transition } from '@utils/transition'; import { config } from '../../global/config'; @@ -146,7 +147,7 @@ export class RouterOutlet implements ComponentInterface, NavOutlet { try { changed = await this.transition(enteringEl, leavingEl, opts); } catch (e) { - console.error(e); + printIonError('[ion-router-outlet] - Exception in commit:', e); } unlock(); return changed; diff --git a/core/src/components/router/router.tsx b/core/src/components/router/router.tsx index 3f30e6f9750..c68c9c4701c 100644 --- a/core/src/components/router/router.tsx +++ b/core/src/components/router/router.tsx @@ -2,6 +2,7 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core'; import { Component, Element, Event, Listen, Method, Prop } from '@stencil/core'; import type { BackButtonEvent } from '@utils/hardware-back-button'; import { debounce } from '@utils/helpers'; +import { printIonError, printIonWarning } from '@utils/logging'; import type { AnimationBuilder } from '../../interface'; import type { NavigationHookResult } from '../route/route-interface'; @@ -166,15 +167,15 @@ export class Router implements ComponentInterface { @Method() async navChanged(direction: RouterDirection): Promise { if (this.busy) { - console.warn('[ion-router] router is busy, navChanged was cancelled'); + printIonWarning('[ion-router] - Router is busy, navChanged was cancelled.'); return false; } const { ids, outlet } = await readNavState(window.document.body); const routes = readRoutes(this.el); const chain = findChainForIDs(ids, routes); if (!chain) { - console.warn( - '[ion-router] no matching URL for ', + printIonWarning( + '[ion-router] - No matching URL for', ids.map((i) => i.id) ); return false; @@ -182,7 +183,7 @@ export class Router implements ComponentInterface { const segments = chainToSegments(chain); if (!segments) { - console.warn('[ion-router] router could not match path because some required param is missing'); + printIonWarning('[ion-router] - Router could not match path because some required param is missing.'); return false; } @@ -232,7 +233,7 @@ export class Router implements ComponentInterface { animation?: AnimationBuilder ): Promise { if (!segments) { - console.error('[ion-router] URL is not part of the routing set'); + printIonError('[ion-router] - URL is not part of the routing set.'); return false; } @@ -253,7 +254,7 @@ export class Router implements ComponentInterface { const routes = readRoutes(this.el); const chain = findChainForSegments(segments, routes); if (!chain) { - console.error('[ion-router] the path does not match any route'); + printIonError('[ion-router] - The path does not match any route.'); return false; } @@ -275,7 +276,7 @@ export class Router implements ComponentInterface { try { changed = await this.writeNavState(node, chain, direction, segments, redirectFrom, index, animation); } catch (e) { - console.error(e); + printIonError('[ion-router] - Exception in safeWriteNavState:', e); } unlock(); return changed; @@ -338,7 +339,7 @@ export class Router implements ComponentInterface { animation?: AnimationBuilder ): Promise { if (this.busy) { - console.warn('[ion-router] router is busy, transition was cancelled'); + printIonWarning('[ion-router] - Router is busy, transition was cancelled.'); return false; } this.busy = true; diff --git a/core/src/components/router/utils/dom.ts b/core/src/components/router/utils/dom.ts index 95bc6fec53f..c10b34d0840 100644 --- a/core/src/components/router/utils/dom.ts +++ b/core/src/components/router/utils/dom.ts @@ -1,4 +1,5 @@ import { componentOnReady } from '@utils/helpers'; +import { printIonError } from '@utils/logging'; import type { AnimationBuilder } from '../../../interface'; @@ -51,7 +52,7 @@ export const writeNavState = async ( } return changed; } catch (e) { - console.error(e); + printIonError('[ion-router] - Exception in writeNavState:', e); return false; } }; diff --git a/core/src/components/segment-button/segment-button.tsx b/core/src/components/segment-button/segment-button.tsx index fb940c9d99b..115040298d3 100644 --- a/core/src/components/segment-button/segment-button.tsx +++ b/core/src/components/segment-button/segment-button.tsx @@ -3,6 +3,7 @@ import { Component, Element, Host, Prop, Method, State, Watch, forceUpdate, h } import type { ButtonInterface } from '@utils/element-interface'; import type { Attributes } from '@utils/helpers'; import { addEventListener, removeEventListener, inheritAttributes } from '@utils/helpers'; +import { printIonError, printIonWarning } from '@utils/logging'; import { hostContext } from '@utils/theme'; import { getIonMode } from '../../global/ionic-global'; @@ -75,7 +76,9 @@ export class SegmentButton implements ComponentInterface, ButtonInterface { // Prevent buttons from being disabled when associated with segment content if (this.contentId && this.disabled) { - console.warn(`Segment Button: Segment buttons cannot be disabled when associated with an .`); + printIonWarning( + `[ion-segment-button] - Segment buttons cannot be disabled when associated with an .` + ); this.disabled = false; } } @@ -102,13 +105,15 @@ export class SegmentButton implements ComponentInterface, ButtonInterface { // If no associated Segment Content exists, log an error and return if (!segmentContent) { - console.error(`Segment Button: Unable to find Segment Content with id="${this.contentId}".`); + printIonError(`[ion-segment-button] - Unable to find Segment Content with id="${this.contentId}".`); return; } // Ensure the found element is a valid ION-SEGMENT-CONTENT if (segmentContent.tagName !== 'ION-SEGMENT-CONTENT') { - console.error(`Segment Button: Element with id="${this.contentId}" is not an element.`); + printIonError( + `[ion-segment-button] - Element with id="${this.contentId}" is not an element.` + ); return; } } diff --git a/core/src/components/select/select.tsx b/core/src/components/select/select.tsx index 0645cd5e56b..df432d8af8c 100644 --- a/core/src/components/select/select.tsx +++ b/core/src/components/select/select.tsx @@ -4,6 +4,7 @@ import type { NotchController } from '@utils/forms'; import { compareOptions, createNotchController, isOptionSelected } from '@utils/forms'; import { focusVisibleElement, renderHiddenInput, inheritAttributes } from '@utils/helpers'; import type { Attributes } from '@utils/helpers'; +import { printIonWarning } from '@utils/logging'; import { actionSheetController, alertController, popoverController, modalController } from '@utils/overlays'; import type { OverlaySelect } from '@utils/overlays-interface'; import { isRTL } from '@utils/rtl'; @@ -426,15 +427,15 @@ export class Select implements ComponentInterface { private createOverlay(ev?: UIEvent): Promise { let selectInterface = this.interface; if (selectInterface === 'action-sheet' && this.multiple) { - console.warn( - `Select interface cannot be "${selectInterface}" with a multi-value select. Using the "alert" interface instead.` + printIonWarning( + `[ion-select] - Interface cannot be "${selectInterface}" with a multi-value select. Using the "alert" interface instead.` ); selectInterface = 'alert'; } if (selectInterface === 'popover' && !ev) { - console.warn( - `Select interface cannot be a "${selectInterface}" without passing an event. Using the "alert" interface instead.` + printIonWarning( + `[ion-select] - Interface cannot be a "${selectInterface}" without passing an event. Using the "alert" interface instead.` ); selectInterface = 'alert'; } diff --git a/core/src/components/split-pane/split-pane.tsx b/core/src/components/split-pane/split-pane.tsx index 7cd33ba580b..e2a02dab7a9 100644 --- a/core/src/components/split-pane/split-pane.tsx +++ b/core/src/components/split-pane/split-pane.tsx @@ -1,5 +1,6 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core'; import { Build, Component, Element, Event, Host, Method, Prop, State, Watch, h } from '@stencil/core'; +import { printIonWarning } from '@utils/logging'; import { getIonMode } from '../../global/ionic-global'; @@ -154,7 +155,7 @@ export class SplitPane implements ComponentInterface { const isMain = contentId !== undefined && child.id === contentId; if (isMain) { if (foundMain) { - console.warn('split pane cannot have more than one main node'); + printIonWarning('[ion-split-pane] - Cannot have more than one main node.'); return; } else { setPaneClass(child, isMain); @@ -163,7 +164,7 @@ export class SplitPane implements ComponentInterface { } } if (!foundMain) { - console.warn('split pane does not have a specified main node'); + printIonWarning('[ion-split-pane] - Does not have a specified main node.'); } } diff --git a/core/src/components/tab/tab.tsx b/core/src/components/tab/tab.tsx index ce507867fdd..a1f6be57a06 100644 --- a/core/src/components/tab/tab.tsx +++ b/core/src/components/tab/tab.tsx @@ -1,6 +1,7 @@ import type { ComponentInterface } from '@stencil/core'; import { Build, Component, Element, Host, Method, Prop, Watch, h } from '@stencil/core'; import { attachComponent } from '@utils/framework-delegate'; +import { printIonError } from '@utils/logging'; import type { ComponentRef, FrameworkDelegate } from '../../interface'; @@ -33,8 +34,8 @@ export class Tab implements ComponentInterface { async componentWillLoad() { if (Build.isDev) { if (this.component !== undefined && this.el.childElementCount > 0) { - console.error( - 'You can not use a lazy-loaded component in a tab and inlined content at the same time.' + + printIonError( + '[ion-tab] - You can not use a lazy-loaded component in a tab and inlined content at the same time.' + `- Remove the component attribute in: ` + ` or` + `- Remove the embedded content inside the ion-tab: ` @@ -66,7 +67,7 @@ export class Tab implements ComponentInterface { try { return attachComponent(this.delegate, this.el, this.component, ['ion-page']); } catch (e) { - console.error(e); + printIonError('[ion-tab] - Exception in prepareLazyLoaded:', e); } } return Promise.resolve(undefined); diff --git a/core/src/components/tabs/tabs.tsx b/core/src/components/tabs/tabs.tsx index 3eebb46d15b..e02e1aa91bd 100644 --- a/core/src/components/tabs/tabs.tsx +++ b/core/src/components/tabs/tabs.tsx @@ -1,5 +1,6 @@ import type { EventEmitter } from '@stencil/core'; import { Component, Element, Event, Host, Method, Prop, State, h } from '@stencil/core'; +import { printIonError } from '@utils/logging'; import type { NavOutlet, RouteID, RouteWrite } from '../router/utils/interface'; import type { TabButtonClickEventDetail } from '../tab-bar/tab-bar-interface'; @@ -210,7 +211,7 @@ const getTab = (tabs: HTMLIonTabElement[], tab: string | HTMLIonTabElement): HTM const tabEl = typeof tab === 'string' ? tabs.find((t) => t.tab === tab) : tab; if (!tabEl) { - console.error(`tab with id: "${tabEl}" does not exist`); + printIonError(`[ion-tabs] - Tab with id: "${tabEl}" does not exist`); } return tabEl; }; diff --git a/core/src/components/toast/animations/utils.ts b/core/src/components/toast/animations/utils.ts index d9057dda0d2..278ef2313fd 100644 --- a/core/src/components/toast/animations/utils.ts +++ b/core/src/components/toast/animations/utils.ts @@ -83,7 +83,7 @@ export function getAnimationPosition( function warnIfAnchorIsHidden(positionAnchor: HTMLElement, toast: HTMLElement) { if (positionAnchor.offsetParent === null) { printIonWarning( - 'The positionAnchor element for ion-toast was found in the DOM, but appears to be hidden. This may lead to unexpected positioning of the toast.', + '[ion-toast] - The positionAnchor element for ion-toast was found in the DOM, but appears to be hidden. This may lead to unexpected positioning of the toast.', toast ); } diff --git a/core/src/components/toast/toast.tsx b/core/src/components/toast/toast.tsx index d0064716181..45ec9fc0643 100644 --- a/core/src/components/toast/toast.tsx +++ b/core/src/components/toast/toast.tsx @@ -4,7 +4,7 @@ import { ENABLE_HTML_CONTENT_DEFAULT } from '@utils/config'; import type { Gesture } from '@utils/gesture'; import { raf } from '@utils/helpers'; import { createLockController } from '@utils/lock-controller'; -import { printIonWarning } from '@utils/logging'; +import { printIonError, printIonWarning } from '@utils/logging'; import { GESTURE, createDelegateController, @@ -498,7 +498,7 @@ export class Toast implements ComponentInterface, OverlayInterface { } if (position === 'middle' && positionAnchor !== undefined) { - printIonWarning('The positionAnchor property is ignored when using position="middle".', this.el); + printIonWarning('[ion-toast] - The positionAnchor property is ignored when using position="middle".', this.el); return undefined; } @@ -511,7 +511,10 @@ export class Toast implements ComponentInterface, OverlayInterface { */ const foundEl = document.getElementById(positionAnchor); if (foundEl === null) { - printIonWarning(`An anchor element with an ID of "${positionAnchor}" was not found in the DOM.`, el); + printIonWarning( + `[ion-toast] - An anchor element with an ID of "${positionAnchor}" was not found in the DOM.`, + el + ); return undefined; } @@ -522,7 +525,7 @@ export class Toast implements ComponentInterface, OverlayInterface { return positionAnchor; } - printIonWarning('Invalid positionAnchor value:', positionAnchor, el); + printIonWarning('[ion-toast] - Invalid positionAnchor value:', positionAnchor, el); return undefined; } @@ -549,7 +552,7 @@ export class Toast implements ComponentInterface, OverlayInterface { return false; } } catch (e) { - console.error(e); + printIonError('[ion-toast] - Exception in callButtonHandler:', e); } } return true; @@ -705,7 +708,7 @@ export class Toast implements ComponentInterface, OverlayInterface { */ if (layout === 'stacked' && startButtons.length > 0 && endButtons.length > 0) { printIonWarning( - 'This toast is using start and end buttons with the stacked toast layout. We recommend following the best practice of using either start or end buttons with the stacked toast layout.', + '[ion-toast] - This toast is using start and end buttons with the stacked toast layout. We recommend following the best practice of using either start or end buttons with the stacked toast layout.', el ); } diff --git a/core/src/global/ionic-global.ts b/core/src/global/ionic-global.ts index 86ae5878a56..901d41fadad 100644 --- a/core/src/global/ionic-global.ts +++ b/core/src/global/ionic-global.ts @@ -1,4 +1,5 @@ import { getMode, setMode } from '@stencil/core'; +import { printIonWarning } from '@utils/logging'; import type { IonicConfig, Mode } from '../interface'; import { isPlatform, setupPlatforms } from '../utils/platform'; @@ -67,7 +68,7 @@ export const initialize = (userConfig: IonicConfig = {}) => { if (isAllowedIonicModeValue(elmMode)) { return elmMode; } else if (isIonicElement(elm)) { - console.warn('Invalid ionic mode: "' + elmMode + '", expected: "ios" or "md"'); + printIonWarning('Invalid ionic mode: "' + elmMode + '", expected: "ios" or "md"'); } } elm = elm.parentElement; diff --git a/core/src/utils/animation/animation.ts b/core/src/utils/animation/animation.ts index c7c57b9f876..4751e724230 100644 --- a/core/src/utils/animation/animation.ts +++ b/core/src/utils/animation/animation.ts @@ -1,3 +1,5 @@ +import { printIonError } from '@utils/logging'; + import { win } from '../browser'; import type { @@ -455,7 +457,7 @@ export const createAnimation = (animationId?: string): Animation => { elements.push((el as any)[i]); } } else { - console.error('Invalid addElement value'); + printIonError('createAnimation - Invalid addElement value.'); } } diff --git a/core/src/utils/hardware-back-button.ts b/core/src/utils/hardware-back-button.ts index dbd5d70783e..2c79e3cfaa7 100644 --- a/core/src/utils/hardware-back-button.ts +++ b/core/src/utils/hardware-back-button.ts @@ -1,5 +1,6 @@ import { win } from '@utils/browser'; import type { CloseWatcher } from '@utils/browser'; +import { printIonError } from '@utils/logging'; import { config } from '../global/config'; @@ -77,7 +78,7 @@ export const startHardwareBackButton = () => { } } } catch (e) { - console.error(e); + printIonError('[ion-app] - Exception in startHardwareBackButton:', e); } }; diff --git a/core/src/utils/helpers.ts b/core/src/utils/helpers.ts index e2a65407c0d..e32956c35eb 100644 --- a/core/src/utils/helpers.ts +++ b/core/src/utils/helpers.ts @@ -1,4 +1,5 @@ import type { EventEmitter } from '@stencil/core'; +import { printIonError } from '@utils/logging'; import type { Side } from '../components/menu/menu-interface'; @@ -287,7 +288,7 @@ export const clamp = (min: number, n: number, max: number) => { export const assert = (actual: any, reason: string) => { if (!actual) { const message = 'ASSERT: ' + reason; - console.error(message); + printIonError(message); debugger; // eslint-disable-line throw new Error(message); } diff --git a/core/src/utils/logging/index.ts b/core/src/utils/logging/index.ts index 4bf58ce6297..d13d6d8d0d1 100644 --- a/core/src/utils/logging/index.ts +++ b/core/src/utils/logging/index.ts @@ -1,6 +1,6 @@ import { config } from '@global/config'; -export const enum LogLevel { +export enum LogLevel { OFF = 'OFF', ERROR = 'ERROR', WARN = 'WARN', diff --git a/core/src/utils/overlays.ts b/core/src/utils/overlays.ts index 4a392ac5370..9dfc0b63edd 100644 --- a/core/src/utils/overlays.ts +++ b/core/src/utils/overlays.ts @@ -2,6 +2,7 @@ import { doc } from '@utils/browser'; import { focusFirstDescendant, focusLastDescendant, focusableQueryString } from '@utils/focus-trap'; import type { BackButtonEvent } from '@utils/hardware-back-button'; import { shouldUseCloseWatcher } from '@utils/hardware-back-button'; +import { printIonError, printIonWarning } from '@utils/logging'; import { config } from '../global/config'; import { getIonMode } from '../global/ionic-global'; @@ -31,7 +32,6 @@ import { getElementRoot, removeEventListener, } from './helpers'; -import { printIonWarning } from './logging'; import { isPlatform } from './platform'; let lastOverlayIndex = 0; @@ -648,7 +648,7 @@ export const dismiss = async ( const presentedOverlays = doc !== undefined ? getPresentedOverlays(doc) : []; /** - * For accessibility, toasts lack focus traps and don’t receive + * For accessibility, toasts lack focus traps and don't receive * `aria-hidden` on the root element when presented. * * All other overlays use focus traps to keep keyboard focus @@ -723,7 +723,7 @@ export const dismiss = async ( overlay.el.lastFocus = undefined; } } catch (err) { - console.error(err); + printIonError(`[${overlay.el.tagName.toLowerCase()}] - `, err); } overlay.el.remove(); @@ -940,7 +940,7 @@ export const createTriggerController = () => { const triggerEl = trigger !== undefined ? document.getElementById(trigger) : null; if (!triggerEl) { printIonWarning( - `A trigger element with the ID "${trigger}" was not found in the DOM. The trigger element must be in the DOM when the "trigger" property is set on an overlay component.`, + `[${el.tagName.toLowerCase()}] - A trigger element with the ID "${trigger}" was not found in the DOM. The trigger element must be in the DOM when the "trigger" property is set on an overlay component.`, el ); return; diff --git a/core/src/utils/sanitization/index.ts b/core/src/utils/sanitization/index.ts index 8d8b2d7992f..acab505d828 100644 --- a/core/src/utils/sanitization/index.ts +++ b/core/src/utils/sanitization/index.ts @@ -1,8 +1,9 @@ +import { printIonError } from '@utils/logging'; + /** * Does a simple sanitization of all elements * in an untrusted string */ - export const sanitizeDOMString = (untrustedString: IonicSafeString | string | undefined): string | undefined => { try { if (untrustedString instanceof IonicSafeString) { @@ -81,7 +82,7 @@ export const sanitizeDOMString = (untrustedString: IonicSafeString | string | un const getInnerDiv = fragmentDiv.querySelector('div'); return getInnerDiv !== null ? getInnerDiv.innerHTML : fragmentDiv.innerHTML; } catch (err) { - console.error(err); + printIonError('sanitizeDOMString', err); return ''; }