public function resolveAssetSourcePathAndTemplate(
        $source,
        $templateName,
        $fileName,
        ParserInterface $parserInterface,
        TemplateDefinition &$templateDefinition
    ) {
        // A simple cache for the path list, to gain some performances
        static $cache = [];

        // The path are categorized, and will be checked in the following order. (see getPossibleAssetSources)
        // - template : the template directory
        // - module_override : the module override directory (template/modules/module_name/...)
        // - module_directory : the current template in the module directory
        // - the default template in the module directory.
        static $pathOrigin = ['template', 'module_override', 'module_directory', 'default_fallback'];

        $templateName = $templateName ?: $templateDefinition->getName();

        $templateDirectories = $parserInterface->getTemplateDirectories($templateDefinition->getType());

        $hash = "$source|$templateName";

        if (! isset($cache[$hash])) {
            // Build a list of all template names, starting with current template
            $templateList = [$templateName => $templateDefinition];

            /** @var TemplateDefinition $tplDef */
            foreach ($templateDefinition->getParentList() as $tplDef) {
                $templateList[$tplDef->getName()] = $tplDef;
            }

            $pathList = [];

            // Get all possible directories to search, including the parent templates ones.
            // The current template is checked firts, then the parent ones.

            /** @var TemplateDefinition $templateDef */
            foreach ($templateList as $tplName => $dummy) {
                $this->getPossibleAssetSources(
                    $templateDirectories,
                    $tplName,
                    $source,
                    $pathList
                );
            }

            $cache[$hash] = [ $pathList, $templateList ];
        } else {
            list($pathList, $templateList) = $cache[$hash];
        }

        // Normalize path separator if required (e.g., / becomes \ on windows)
        $fileName = $this->fixPathSeparator($fileName);

        /* Absolute paths are not allowed. This may be a mistake, such as '/assets/...' instead of 'assets/...'. Forgive it.  */
        $fileName = ltrim($fileName, DS);

        /* Navigating in the server's directory tree is not allowed :) */
        if (preg_match('!\.\.\\'.DS.'!', $fileName)) {
            // This time, we will not forgive.
            throw new \InvalidArgumentException("Relative paths are not allowed in assets names.");
        }

        // Find the first occurrence of the file in the directory list.
        foreach ($pathOrigin as $origin) {
            if (isset($pathList[$origin])) {
                foreach ($pathList[$origin] as $pathInfo) {
                    list($tplName, $path) = $pathInfo;

                    if ($this->filesExist($path, $fileName)) {
                        // Got it ! Save the template where the asset was found and return !
                        $templateDefinition = $templateList[$tplName];

                        return $path;
                    }
                }
            }
        }

        // Not found !
        return null;
    }