Repository: plotly/dash
File: dash/dash-renderer/src/utils/TreeContainer.ts (TypeScript)

Code before:
    if (idprops && isLoadingComponent(componentLayout)) {
        return {
            is_loading: true,
            prop_name: idprops.property,
            component_name: stringifyId(idprops.id)
        };
    }

    return NULL_LOADING_STATE;
}

export const getLoadingHash = (componentPath: any, loadingMap: any) =>
    (
        ((loadingMap &&
            (path(componentPath, loadingMap) as any)
                ?.__dashprivate__idprops__) ??
            []) as any[]
    )
        .map(({id, property}) => `${id}.${property}`)
        .join(',');

export function validateComponent(componentDefinition: any) {
    if (type(componentDefinition) === 'Array') {
        throw new Error(
            'The children property of a component is a list of lists, instead ' +
                'of just a list. Check for trailing commas, or ' +
                'check the component that has the following contents ' +
                'and remove one of the levels of nesting: \n' +
                JSON.stringify(componentDefinition, null, 2)
        );
    }
    if (
        type(componentDefinition) === 'Object' &&
        !(
            has('namespace', componentDefinition) &&
            has('type', componentDefinition) &&
            has('props', componentDefinition)
        )
    ) {
        throw new Error(
            'An object was provided as `children` instead of a component, ' +
                'string, or number (or list of those). ' +
                'Check the children property that looks something like:\n' +
                JSON.stringify(componentDefinition, null, 2)
        );
    }
}


Review comment (suggestion):
This isn't really an "or" condition.  If a tuple is supplied as children, the content  will be shown in the error message details.

How about something like:

> The children property of a component is a list of lists, instead of just a list. Review the component with the following content and remove one of the levels of nesting.  Note that tuples are converted to a list of lists.  Check for trailing commas.

Code after:
    if (idprops && isLoadingComponent(componentLayout)) {
        return {
            is_loading: true,
            prop_name: idprops.property,
            component_name: stringifyId(idprops.id)
        };
    }

    return NULL_LOADING_STATE;
}

export const getLoadingHash = (componentPath: any, loadingMap: any) =>
    (
        ((loadingMap &&
            (path(componentPath, loadingMap) as any)
                ?.__dashprivate__idprops__) ??
            []) as any[]
    )
        .map(({id, property}) => `${id}.${property}`)
        .join(',');

export function validateComponent(componentDefinition: any) {
    if (type(componentDefinition) === 'Array') {
        throw new Error(
            'The children property of a component is a list of lists, instead ' +
                'of just a list. This can sometimes be due to a trailing comma. ' +
                'Check the component that has the following contents ' +
                'and remove one of the levels of nesting: \n' +
                JSON.stringify(componentDefinition, null, 2)
        );
    }
    if (
        type(componentDefinition) === 'Object' &&
        !(
            has('namespace', componentDefinition) &&
            has('type', componentDefinition) &&
            has('props', componentDefinition)
        )
    ) {
        throw new Error(
            'An object was provided as `children` instead of a component, ' +
                'string, or number (or list of those). ' +
                'Check the children property that looks something like:\n' +
                JSON.stringify(componentDefinition, null, 2)
        );
    }
}