def spec_update_sourcepath_filter_loaderplugins(
        spec, sourcepath_map, sourcepath_map_key,
        loaderplugin_sourcepath_map_key=LOADERPLUGIN_SOURCEPATH_MAPS):
    """
    Take an existing spec and a sourcepath mapping (that could be
    produced via calmjs.dist.*_module_registry_dependencies functions)
    and split out the keys that does not contain loaderplugin syntax and
    assign it to the spec under sourcepath_key.

    For the parts with loader plugin syntax (i.e. modnames (keys) that
    contain a '!' character), they are instead stored under a different
    mapping under its own mapping identified by the plugin_name.  The
    mapping under loaderplugin_sourcepath_map_key will contain all
    mappings of this type.

    The resolution for the handlers will be done through the loader
    plugin registry provided via spec[CALMJS_LOADERPLUGIN_REGISTRY] if
    available, otherwise the registry instance will be acquired through
    the main registry using spec[CALMJS_LOADERPLUGIN_REGISTRY_NAME].

    For the example sourcepath_map input:

    sourcepath = {
        'module': 'something',
        'plugin!inner': 'inner',
        'plugin!other': 'other',
        'plugin?query!question': 'question',
        'plugin!plugin2!target': 'target',
    }

    The following will be stored under the following keys in spec:

    spec[sourcepath_key] = {
        'module': 'something',
    }

    spec[loaderplugin_sourcepath_map_key] = {
        'plugin': {
            'plugin!inner': 'inner',
            'plugin!other': 'other',
            'plugin?query!question': 'question',
            'plugin!plugin2!target': 'target',
        },
    }

    The goal of this function is to aid in processing each of the plugin
    types by batch, one level at a time.  It is up to the handler itself
    to trigger further lookups as there are implementations of loader
    plugins that do not respect the chaining mechanism, thus a generic
    lookup done at once may not be suitable.

    Note that nested/chained loaderplugins are not immediately grouped
    as they must be individually handled given that the internal syntax
    are generally proprietary to the outer plugin.  The handling will be
    dealt with at the Toolchain.compile_loaderplugin_entry method
    through the associated handler call method.

    Toolchain implementations may either invoke this directly as part
    of the prepare step on the required sourcepaths values stored in the
    spec, or implement this at a higher level before invocating the
    toolchain instance with the spec.
    """

    default = dict_setget_dict(spec, sourcepath_map_key)
    registry = spec_update_loaderplugin_registry(spec)

    # it is more loaderplugin_sourcepath_maps
    plugins = dict_setget_dict(spec, loaderplugin_sourcepath_map_key)

    for modname, sourcepath in sourcepath_map.items():
        parts = modname.split('!', 1)
        if len(parts) == 1:
            # default
            default[modname] = sourcepath
            continue

        # don't actually do any processing yet.
        plugin_name = registry.to_plugin_name(modname)
        plugin = dict_setget_dict(plugins, plugin_name)
        plugin[modname] = sourcepath
Take an existing spec and a sourcepath mapping (that could be
    produced via calmjs.dist.*_module_registry_dependencies functions)
    and split out the keys that does not contain loaderplugin syntax and
    assign it to the spec under sourcepath_key.

    For the parts with loader plugin syntax (i.e. modnames (keys) that
    contain a '!' character), they are instead stored under a different
    mapping under its own mapping identified by the plugin_name.  The
    mapping under loaderplugin_sourcepath_map_key will contain all
    mappings of this type.

    The resolution for the handlers will be done through the loader
    plugin registry provided via spec[CALMJS_LOADERPLUGIN_REGISTRY] if
    available, otherwise the registry instance will be acquired through
    the main registry using spec[CALMJS_LOADERPLUGIN_REGISTRY_NAME].

    For the example sourcepath_map input:

    sourcepath = {
        'module': 'something',
        'plugin!inner': 'inner',
        'plugin!other': 'other',
        'plugin?query!question': 'question',
        'plugin!plugin2!target': 'target',
    }

    The following will be stored under the following keys in spec:

    spec[sourcepath_key] = {
        'module': 'something',
    }

    spec[loaderplugin_sourcepath_map_key] = {
        'plugin': {
            'plugin!inner': 'inner',
            'plugin!other': 'other',
            'plugin?query!question': 'question',
            'plugin!plugin2!target': 'target',
        },
    }

    The goal of this function is to aid in processing each of the plugin
    types by batch, one level at a time.  It is up to the handler itself
    to trigger further lookups as there are implementations of loader
    plugins that do not respect the chaining mechanism, thus a generic
    lookup done at once may not be suitable.

    Note that nested/chained loaderplugins are not immediately grouped
    as they must be individually handled given that the internal syntax
    are generally proprietary to the outer plugin.  The handling will be
    dealt with at the Toolchain.compile_loaderplugin_entry method
    through the associated handler call method.

    Toolchain implementations may either invoke this directly as part
    of the prepare step on the required sourcepaths values stored in the
    spec, or implement this at a higher level before invocating the
    toolchain instance with the spec.