Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
80.33% covered (warning)
80.33%
49 / 61
72.73% covered (warning)
72.73%
8 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
Cli
80.33% covered (warning)
80.33%
49 / 61
72.73% covered (warning)
72.73%
8 / 11
32.55
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 run
80.77% covered (warning)
80.77%
21 / 26
0.00% covered (danger)
0.00%
0 / 1
10.71
 runCss
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 runCssRemap
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
2.09
 runJs
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 runJsMapWeb
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 runJsMapRaw
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 help
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 error
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 output
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getExitCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php declare( strict_types=1 );
2/**
3 * Copyright 2021 Timo Tijhof
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * @file
18 * @license Apache-2.0
19 */
20
21namespace Wikimedia\Minify;
22
23/**
24 * Implementation of `minify` CLI.
25 *
26 * @internal For use by `bin/minify` only.
27 */
28final class Cli {
29    /** @var int $exitCode */
30    private $exitCode = 0;
31    /** @var resource $in */
32    private $in;
33    /** @var resource $out */
34    private $out;
35    /** @var string $self */
36    private $self;
37    /** @var string $command */
38    private $command;
39    /** @var string[] $params */
40    private $params;
41
42    /**
43     * @param resource $in An open stream for reading input with `fgets()`
44     * @param resource $out An open stream for writing output with `fwrite()`
45     * @param string[] $argv
46     */
47    public function __construct( $in, $out, array $argv ) {
48        $this->in = $in;
49        $this->out = $out;
50        $this->self = basename( $argv[0] ?? '/minify' );
51        $this->command = $argv[1] ?? '';
52        $this->params = array_slice( $argv, 2 );
53    }
54
55    /** Perform the specified command. */
56    public function run(): void {
57        try {
58            switch ( $this->command ) {
59                case 'css':
60                    $this->runCss( ...$this->params );
61                    break;
62                case 'css-remap':
63                    $this->runCssRemap( ...$this->params );
64                    break;
65                case 'js':
66                    $this->runJs( ...$this->params );
67                    break;
68                case 'jsmap-web':
69                    $this->runJsMapWeb( ...$this->params );
70                    break;
71                case 'jsmap-raw':
72                    $this->runJsMapRaw( ...$this->params );
73                    break;
74                case '':
75                case 'help':
76                    $this->exitCode = 1;
77                    $this->help();
78                    break;
79                default:
80                    $this->error( 'Unknown command' );
81                    break;
82            }
83        } catch ( \Throwable $e ) {
84            $this->exitCode = 1;
85            $this->output( (string)$e );
86        }
87    }
88
89    private function runCss( string $file = null ): void {
90        $data = $file === null ? stream_get_contents( $this->in ) : file_get_contents( $file );
91        $this->output( CSSMin::minify( $data ) );
92    }
93
94    private function runCssRemap( string $file = null ): void {
95        if ( $file === null ) {
96            $this->error( 'Remapping requires a filepath' );
97            return;
98        }
99        $fulldir = dirname( realpath( $file ) );
100        $data = file_get_contents( $file );
101        $data = CSSMin::remap( $data, $fulldir, $fulldir );
102        $this->output( CSSMin::minify( $data ) );
103    }
104
105    private function runJs( string $file = null ): void {
106        $data = $file === null ? stream_get_contents( $this->in ) : file_get_contents( $file );
107        $this->output( JavaScriptMinifier::minify( $data ) );
108    }
109
110    private function runJsMapWeb( string $file = null ): void {
111        $data = $file === null ? stream_get_contents( $this->in ) : file_get_contents( $file );
112        $sourceName = $file === null ? 'file.js' : basename( $file );
113        $mapper = JavaScriptMinifier::createSourceMapState();
114        $mapper->addSourceFile( $sourceName, $data, true );
115        $this->output( rtrim( $mapper->getSourceMap(), "\n" ) );
116    }
117
118    private function runJsMapRaw( string $file = null ): void {
119        $data = $file === null ? stream_get_contents( $this->in ) : file_get_contents( $file );
120        $sourceName = $file === null ? 'file.js' : basename( $file );
121        $mapper = JavaScriptMinifier::createSourceMapState();
122        $mapper->addSourceFile( $sourceName, $data, true );
123        $this->output( rtrim( $mapper->getRawSourceMap(), "\n" ) );
124    }
125
126    private function help(): void {
127        $this->output( <<<TEXT
128usage: {$this->self} <command>
129
130commands:
131   css [<file>]        Minify input data as CSS code, and write to output.
132                       Reads from stdin by default, or can read from a file.
133   css-remap <file>    Remap and process any "@embed" comments in a CSS file,
134                       and write the minified code to output.
135   js  [<file>]        Minify input data as JavaScript code, write to output.
136                       Reads from stdin by default, or can read from a file.
137   jsmap-raw [<file>]  Minify JavaScript code and write a raw source map to
138                       output. Such a source map should not be delivered over
139                       HTTP due to XSSI concerns.
140   jsmap-web [<file>]  Minify JavaScript code and write a source map with XSSI
141                       prefix.
142TEXT
143        );
144    }
145
146    private function error( string $text ): void {
147        $this->exitCode = 1;
148        $this->output( "\n{$this->self} error: $text\n\n------\n" );
149        $this->help();
150    }
151
152    private function output( string $text ): void {
153        fwrite( $this->out, $text . "\n" );
154    }
155
156    /** @return int */
157    public function getExitCode(): int {
158        return $this->exitCode;
159    }
160}