ischenko/yii2-jsloader-requirejs

View on GitHub
src/requirejs/Config.php

Summary

Maintainability
B
5 hrs
Test Coverage
<?php
/**
 * @copyright Copyright (c) 2016 Roman Ishchenko
 * @license https://github.com/ischenko/yii2-jsloader-requirejs/blob/master/LICENSE
 * @link https://github.com/ischenko/yii2-jsloader-requirejs#readme
 */

namespace ischenko\yii2\jsloader\requirejs;

use ischenko\yii2\jsloader\FilterInterface;
use ischenko\yii2\jsloader\ModuleInterface;
use yii\web\JsExpression;

/**
 * RequireJs-specific implementation of the configuration
 *
 * @author Roman Ishchenko <roman@ishchenko.ck.ua>
 * @since 1.0
 *
 * @method Module|null getModule($name)
 * @method Module[] getModules(FilterInterface $filter = null)
 */
class Config extends \ischenko\yii2\jsloader\base\Config
{
    /**
     * @var array a list of valid options
     *
     * TODO: create setter and getter for map and config
     */
    static private $validOptions = [
        'map' => 1,
        'config' => 1,
        'enforceDefine' => 1,
        'waitSeconds' => 1,
        'context' => 1,
        'deps' => 1,
        'xhtml' => 1,
        'urlArgs' => 1,
        'scriptType' => 1,
        'skipDataMain' => 1,
    ];
    /**
     * @var array a list of other configuration options
     */
    protected $attributes = [];

    /**
     * Common getter for configuration options
     *
     * @param string $name
     * @return mixed
     */
    public function __get($name)
    {
        if (isset(self::$validOptions[$name])) {
            return $this->attributes[$name] ?? null;
        }

        return parent::__get($name);
    }

    /**
     * Common setter for configuration options
     *
     * @param string $name
     * @param mixed $value
     */
    public function __set($name, $value)
    {
        if (isset(self::$validOptions[$name])) {
            $this->attributes[$name] = $value;
            return;
        }

        parent::__set($name, $value);
    }

    /**
     * Setter for callback option
     *
     * @param string|JsExpression $value
     *
     * @return $this
     */
    public function setCallback($value)
    {
        if (!($value instanceof JsExpression)) {
            $value = new JsExpression($value);
        }

        $this->attributes['callback'] = $value;

        return $this;
    }

    /**
     * @return JsExpression|null
     */
    public function getCallback()
    {
        return $this->attributes['callback'] ?? null;
    }

    /**
     * Builds configuration set into an array
     *
     * @return array
     */
    public function toArray(): array
    {
        $config = $this->attributes;

        if (!empty($this->baseUrl)) {
            $config['baseUrl'] = $this->baseUrl;
        }

        foreach ($this->getModules() as $module) {
            // Generate shim section
            if (($shimConfig = $this->renderShim($module)) !== []) {
                $config['shim'][$module->getAlias()] = $shimConfig;
            }

            // Generate paths section
            if (($pathsConfig = $this->renderPaths($module, $config)) !== []) {
                $config['paths'][$module->getAlias()] = $pathsConfig;
            }
        }

        return $config;
    }

    /**
     * Adds new module into configuration
     *
     * If passed a string a new module will be created
     *
     * @param ModuleInterface|string $module an instance of module to be added or name of a module to be created and added
     *
     * @return Module
     */
    public function addModule($module): ModuleInterface
    {
        if (!($module instanceof ModuleInterface)) {
            $module = new Module($module);
        }

        return parent::addModule($module);
    }

    /**
     * @see http://requirejs.org/docs/api.html#config-paths
     *
     * @param array $data
     *
     * @return $this
     */
    public function setPaths(array $data)
    {
        foreach ($data as $moduleName => $files) {
            if (empty($files)) {
                continue;
            }

            $file = $files;
            $fallback = [];

            if (is_array($files)) {
                $file = array_shift($files);
                $fallback = $files;
            }

            if (!($module = $this->getModule($moduleName))) {
                $module = $this->addModule($moduleName);
            }

            $module->clearFiles();
            $module->clearFallbackFiles();
            $module->addFile($file);

            if ($fallback !== []) {
                $module->addFallbackFiles($fallback);
            }
        }

        return $this;
    }

    /**
     * @see http://requirejs.org/docs/api.html#config-shim
     *
     * @param array $data
     *
     * @return $this
     */
    public function setShim(array $data)
    {
        foreach ($data as $moduleName => $properties) {
            if (empty($properties)) {
                continue;
            }

            if (!($module = $this->getModule($moduleName))) {
                $module = $this->addModule($moduleName);
            }

            $module->setExports(null)->clearDependencies();

            if (!empty($properties['deps'])) {
                foreach ((array)$properties['deps'] as $dep) {
                    if (!($depModule = $this->getModule($dep))) {
                        $depModule = $this->addModule(md5($dep))->addFile($dep);
                    }

                    $module->addDependency($depModule);
                }
            }

            if (!empty($properties['init'])) {
                $module->setInit($properties['init']);
            }

            if (!empty($properties['exports'])) {
                $module->setExports($properties['exports']);
            }
        }

        return $this;
    }

    /**
     * Performs generation of the shim section
     *
     * @param Module $module
     * @return array
     */
    private function renderShim(Module $module)
    {
        $shimConfig = [];

        foreach ($module->getDependencies() as $dependency) {
            if (!isset($shimConfig['deps'])) {
                $shimConfig['deps'] = [];
            }

            $shimConfig['deps'][] = $dependency->getAlias();
        }

        if (($exports = $module->getExports()) !== null) {
            $shimConfig['exports'] = $exports;
        }

        if (($init = $module->getInit()) !== null) {
            $shimConfig['init'] = $init;
        }

        return $shimConfig;
    }

    /**
     * Performs generation of the paths section
     *
     * @param Module $module
     * @param array $config
     *
     * @return array
     */
    private function renderPaths(Module $module, &$config)
    {
        $options = $module->getOptions();
        $files = array_keys($module->getFiles());

        if (!empty($options['baseUrl']) && $files === []) {
            return $options['baseUrl'];
        }

        $paths = [$this->removeJsExtension(array_pop($files))];

        foreach ($module->getFallbackFiles() as $file) {
            $paths[] = $this->removeJsExtension($file);
        }

        // Add rest of the files as dependencies
        if ($files !== []) {
            $moduleName = $module->getAlias();

            if (!isset($config['shim'][$moduleName]['deps'])) {
                $config['shim'][$moduleName]['deps'] = [];
            }

            foreach ($files as $file) {
                if (!isset($parentDepends) || !isset($options['async'])) {
                    $parentDepends = $config['shim'][$moduleName]['deps'];
                }

                $config['shim'][$file]['deps'] = $parentDepends;
                $config['shim'][$moduleName]['deps'][] = $file;
            }
        }

        return $paths;
    }

    /**
     * Removes .js extension for a file
     *
     * @param string $file
     * @return string
     */
    private function removeJsExtension($file)
    {
        return preg_replace('/\.js(?:\?.*)?$/', '', $file);
    }
}