在 Symfony/Twig 响应中缩小/丑化嵌入 JavaScript

Minify / Uglify embedded JavaScript in Symfony/Twig response

在我的 Symfony 2.8 项目中使用 Assetic 时,压缩链接的 JavaScriptCSS 文件没什么大不了的。但是使用 script 标签直接嵌入页面的脚本呢?这些脚本不会以任何方式修改。

是否也可以 compress/modify/uglify 这些脚本?

当然,我可以简单地将这些脚本移动到单独的文件中,从而也对它们应用 Assetic 过滤器。但在某些情况下,将脚本直接放在 HTML / Twig 模板中会很方便。

那么,有没有现有的解决方案可以在不移动它们的情况下过滤这些脚本?

因为我找不到解决这个问题的方法,所以我终于设法创建了自己的:将自定义标签添加到我的(现有)Twig 扩展中,以将 Assetics uglifyJS2 过滤器应用于嵌入式脚本:

第 1 步: 创建一个 Twig TokenParserNode

class UglifyTokenParser extends Twig_TokenParser {
    private $enabled;    

    public function __construct($enabled = true) {
        $this->enabled = (bool) $enabled;
    }


    public function parse(Twig_Token $token) {
        $lineNumber = $token->getLine();

        $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
        $body = $this->parser->subparse(function (Twig_Token $token) {
            return $token->test('enduglify');
        }, true);

        $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
        if ($this->enabled) {
            $node = new UglifyNode($body, $lineNumber, $this->getTag());
            return $node;
        }

        return $body;
    }    


    public function getTag() {
        return 'uglify';
    }
}

class UglifyNode extends \Twig_Node {
    public function __construct(Twig_Node $body, $lineNumber, $tag = 'uglify') {
        parent::__construct(array('body' => $body), array(), $lineNumber, $tag);
    }

    public function compile(Twig_Compiler $compiler) {      
        $compiler
            ->addDebugInfo($this)
            ->write("ob_start();\n")                
            ->subcompile($this->getNode('body'))
            ->write("echo $context['_uglifier']->uglify(trim(ob_get_clean()));\n");
    }
}

第2步:添加Uglifierclass,Node使用它来将内容传递给AsseticUglifyJS2Filter

class Uglifier {
    private $filter;
    private $asset;
    private $enabled;

    public function __construct(FilterInterface $filter, $endabled) {
        $this->filter = $filter;
        $this->asset = new UglifierAsset(array($this->filter));
        $this->enabled = $endabled;
    }

    public function uglify($content) {
        if ($this->enabled) {
            $this->asset->loadContent($content);
            $uglified = $this->asset->dump();       
            return $uglified;
        } else {
            return $content;
        }
    }
}


class UglifierAsset extends BaseAsset {
    public function __construct($filters = array())  {
        parent::__construct($filters);
    }

    private $theContent;
    public function loadContent($content) {
        $this->theContent = $content;
        $this->load();
    }

    public function load(FilterInterface $additionalFilter = null) {
        $this->doLoad($this->theContent);
    }

    public function getLastModified() {
        return 0;
    }
}

第 3 步: 创建 Uglifier 作为 sService,将其传递给 Twig Extension 并在扩展中实现自定义标记

app.twig_extension:
    class: AppBundle\Twig\AppExtension
    public: false
    arguments: [ "@app.twig_extension.uglifier" ]
    tags:
        - { name: twig.extension }


app.twig_extension.uglifier:
    class: AppBundle\Twig\uglify\Uglifier
    arguments: [ "@assetic.filter.uglifyjs2", "%twig_extension.unglifier.enabled%" ]


class AppExtension extends \Twig_Extension implements \Twig_Extension_GlobalsInterface {
    private $uglifier;
    public function __construct(Uglifier $uglifier) {
        $this->uglifier = $uglifier;
    }

    public function getGlobals() {      
        return array(
            '_uglifier' => $this->uglifier,
        );
    }

    public function getTokenParsers() {
        return array(new UglifyTokenParser());
    }
} 

第 4 步: 使用新的 `{% uglify %} 标签包装嵌入式脚本

{# before #}
<script type="text/javascript">
    // some JS
</script>

{# after #}
<script type="text/javascript">
    {% uglify %}
        // some JS
    {% enduglify %}
</script>

完成