SetupCommand::createDirFromBlueprint()   D
last analyzed

Complexity

Conditions 9
Paths 18

Size

Total Lines 39
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 9.2055

Importance

Changes 0
Metric Value
dl 0
loc 39
ccs 19
cts 22
cp 0.8636
rs 4.909
c 0
b 0
f 0
cc 9
eloc 22
nc 18
nop 4
crap 9.2055
1
<?php
2
3
namespace Rj\FrontendBundle\Command;
4
5
use Rj\FrontendBundle\Command\Options\SimpleOptionHelper;
6
use Rj\FrontendBundle\Command\Options\ChoiceOptionHelper;
7
use Symfony\Component\Console\Command\Command;
8
use Symfony\Component\Console\Input\InputInterface;
9
use Symfony\Component\Console\Input\InputOption;
10
use Symfony\Component\Console\Output\OutputInterface;
11
use Symfony\Component\Templating\EngineInterface;
12
use Symfony\Component\Templating\PhpEngine;
13
use Symfony\Component\Templating\TemplateNameParser;
14
use Symfony\Component\Templating\Loader\FilesystemLoader;
15
16
class SetupCommand extends Command
17
{
18
    /**
19
     * @var EngineInterface
20
     */
21
    private $templating;
22
23
    /**
24
     * @var string|null
25
     */
26
    private $rootDir = null;
27
28
    /**
29
     * @param string|null $name
30
     */
31 14
    public function __construct($name = null)
32
    {
33 14
        parent::__construct($name);
34
35 14
        $this->templating = new PhpEngine(
36 14
            new TemplateNameParser(),
37 14
            new FilesystemLoader([__DIR__.'/../Resources/blueprints/%name%'])
38
        );
39 14
    }
40
41
    /**
42
     * @param string $path
43
     */
44 14
    public function setRootDir($path)
45
    {
46 14
        $this->rootDir = $path;
47 14
    }
48
49
    /**
50
     * {@inheritdoc}
51
     */
52 14
    protected function configure()
53
    {
54
        $this
55 14
            ->setName('rj_frontend:setup')
56 14
            ->setDescription('Generate the configuration for the asset pipeline')
57 14
            ->addOption(
58 14
                'dry-run',
59 14
                null,
60 14
                InputOption::VALUE_NONE,
61 14
                'Output which commands would have been run instead of running them'
62
            )
63 14
            ->addOption(
64 14
                'force',
65 14
                null,
66 14
                InputOption::VALUE_NONE,
67 14
                'Force execution'
68
            )
69 14
            ->addOption(
70 14
                'src-dir',
71 14
                null,
72 14
                InputOption::VALUE_REQUIRED,
73 14
                'Path to the directory containing the source assets [e.g. '.$this->getDefaultOption('src-dir').']'
74
            )
75 14
            ->addOption(
76 14
                'dest-dir',
77 14
                null,
78 14
                InputOption::VALUE_REQUIRED,
79 14
                'Path to the directory containing the compiled assets [e.g. '.$this->getDefaultOption('dest-dir').']'
80
            )
81 14
            ->addOption(
82 14
                'pipeline',
83 14
                null,
84 14
                InputOption::VALUE_REQUIRED,
85 14
                'Asset pipeline to use [only gulp is available at the moment]'
86
            )
87 14
            ->addOption(
88 14
                'csspre',
89 14
                null,
90 14
                InputOption::VALUE_REQUIRED,
91 14
                'CSS preprocessor to use [sass, less or none]'
92
            )
93 14
            ->addOption(
94 14
                'coffee',
95 14
                null,
96 14
                InputOption::VALUE_REQUIRED,
97 14
                'Use the CoffeeScript compiler [true or false]'
98
            )
99
        ;
100 14
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105 2
    protected function interact(InputInterface $input, OutputInterface $output)
106
    {
107 2
        $simpleOptionHelper = new SimpleOptionHelper($this, $input, $output);
108 2
        $choiceOptionHelper = new ChoiceOptionHelper($this, $input, $output);
109
110
        $simpleOptionHelper
111 2
            ->setDefaultValue($this->getDefaultOption('src-dir'))
112 2
            ->setOption(
113 2
                'src-dir',
114 2
                'Path to the directory containing the source assets [default is '.$this->getDefaultOption('src-dir').']'
115
            )
116
        ;
117
118
        $simpleOptionHelper
119 2
            ->setDefaultValue($this->getDefaultOption('dest-dir'))
120 2
            ->setOption(
121 2
                'dest-dir',
122 2
                'Path to the directory containing the compiled assets [default is '.$this->getDefaultOption('dest-dir').']'
123
            )
124
        ;
125
126
        $choiceOptionHelper
127 2
            ->setAllowedValues(['gulp'])
128 2
            ->setErrorMessage('%s is not a supported asset pipeline')
129 2
            ->setOption(
130 2
                'pipeline',
131 2
                'Asset pipeline to use [only gulp is available at the moment]'
132
            )
133
        ;
134
135
        $choiceOptionHelper
136 2
            ->setAllowedValues(['sass', 'less', 'none'])
137 2
            ->setErrorMessage('%s is not a supported CSS preprocessor')
138 2
            ->setOption(
139 2
                'csspre',
140 2
                'CSS preprocessor to use [default is '.$this->getDefaultOption('csspre').']'
141
            )
142
        ;
143
144
        $choiceOptionHelper
145 2
            ->setAllowedValues(['false', 'true'])
146 2
            ->setErrorMessage('%s is not a supported value for --coffee. Use either true or false')
147 2
            ->setOption(
148 2
                'coffee',
149 2
                'Whether to use the CoffeeScript compiler [default is '.$this->getDefaultOption('coffee').']'
150
            )
151
        ;
152
153 2
        $output->writeln('');
154 2
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159 14
    protected function execute(InputInterface $input, OutputInterface $output)
160
    {
161 14
        $this->processOptions($input);
162
163 14
        $output->writeln('<info>Selected options are:</info>');
164 14
        $output->writeln('src-dir:  '.$input->getOption('src-dir'));
165 14
        $output->writeln('dest-dir: '.$input->getOption('dest-dir'));
166 14
        $output->writeln('pipeline: '.$input->getOption('pipeline'));
167 14
        $output->writeln('csspre:   '.$input->getOption('csspre'));
168 14
        $output->writeln('coffee:   '.($input->getOption('coffee') ? 'true' : 'false'));
169
170 14
        if (!preg_match('|web/.+|', $input->getOption('dest-dir'))) {
171 3
            throw new \InvalidArgumentException("'dest-dir' must be a directory under web/");
172
        }
173
174 11
        $output->writeln('');
175 11
        $this->createSourceTree($input, $output);
176 11
        $this->createBuildFile($input, $output);
177 11
        $this->createPackageJson($input, $output);
178 11
        $this->createBowerJson($input, $output);
179
180 11
        $output->writeln('');
181 11
        $output->writeln('<info>All files generated.</info>');
182 11
        $output->writeln('');
183 11
        $output->writeln('<comment>Make sure to install dependencies:</comment>');
184 11
        $output->writeln('');
185 11
        $output->writeln('<comment>  npm install</comment>');
186 11
        $output->writeln('');
187 11
    }
188
189
    /**
190
     * @param InputInterface  $input
191
     * @param OutputInterface $output
192
     */
193 11
    private function createSourceTree(InputInterface $input, OutputInterface $output)
194
    {
195 11
        $blueprints = __DIR__.'/../Resources/blueprints';
196 11
        $dryRun = $input->getOption('dry-run');
197 11
        $base = $input->getOption('src-dir');
198
199 11
        $output->writeln($dryRun
200 5
            ? '<info>Would have created directory tree for source assets:</info>'
201 11
            : '<info>Creating directory tree for source assets:</info>'
202
        );
203
204 11
        $blueprintDir = "$blueprints/images";
205 11
        $this->createDirFromBlueprint($input, $output, $blueprintDir, "$base/images");
206
207 11
        $blueprintDir = "$blueprints/stylesheets/".$input->getOption('csspre');
208 11
        $this->createDirFromBlueprint($input, $output, $blueprintDir, "$base/stylesheets");
209
210 11
        $blueprintDir = "$blueprints/scripts/";
211 11
        $blueprintDir .= $input->getOption('coffee') ? 'coffee' : 'js';
212 11
        $this->createDirFromBlueprint($input, $output, $blueprintDir, "$base/scripts");
213
214 11
        $output->writeln('');
215 11
    }
216
217
    /**
218
     * @param InputInterface  $input
219
     * @param OutputInterface $output
220
     */
221 11 View Code Duplication
    private function createBuildFile(InputInterface $input, OutputInterface $output)
222
    {
223
        $files = [
224 11
            'gulp' => 'gulp/gulpfile.js',
225
        ];
226
227 11
        $this->createFileFromTemplate($input, $output, 'pipelines/'.$files[$input->getOption('pipeline')]);
228 11
    }
229
230
    /**
231
     * @param InputInterface  $input
232
     * @param OutputInterface $output
233
     */
234 11 View Code Duplication
    private function createPackageJson(InputInterface $input, OutputInterface $output)
235
    {
236
        $files = [
237 11
            'gulp' => 'gulp/package.json',
238
        ];
239
240 11
        $this->createFileFromTemplate($input, $output, 'pipelines/'.$files[$input->getOption('pipeline')]);
241 11
    }
242
243
    /**
244
     * @param InputInterface  $input
245
     * @param OutputInterface $output
246
     */
247 11
    private function createBowerJson(InputInterface $input, OutputInterface $output)
248
    {
249 11
        $this->createFileFromTemplate($input, $output, 'bower.json');
250 11
    }
251
252
    /**
253
     * @param InputInterface  $input
254
     * @param OutputInterface $output
255
     * @param string          $blueprintDir
256
     * @param string          $targetDir
257
     */
258 11
    private function createDirFromBlueprint(InputInterface $input, OutputInterface $output, $blueprintDir, $targetDir)
259
    {
260 11
        $dryRun = $input->getOption('dry-run');
261
262 11
        if (!$dryRun && !file_exists($targetDir)) {
263 6
            mkdir($targetDir, 0777, true);
264
        }
265
266 11
        foreach (preg_grep('/^\.?\w+/', scandir($blueprintDir)) as $entry) {
267 11
            $target = $entry;
268
269 11
            $isPhpTemplate = substr($entry, strrpos($entry, '.')) === '.php';
270 11
            if ($isPhpTemplate) {
271 11
                $entry = str_replace('.php', '', $entry);
272 11
                $target = str_replace('.php', '', $target);
273
            }
274
275 11
            $entry = $blueprintDir.'/'.$entry;
276 11
            $target = $targetDir.'/'.$target;
277
278 11
            if (!$dryRun) {
279 6
                if ($isPhpTemplate) {
280 6
                    $this->renderTemplate($input, $output, $entry, $target);
281
                } else {
282 6
                    if (file_exists($target) && !$input->getOption('force')) {
283
                        $output->writeln(
284
                            "<error>$target already exists. Run this command with --force to overwrite</error>
285
                        ");
286
287
                        continue;
288
                    }
289
290 6
                    copy($entry, $target);
291
                }
292
            }
293
294 11
            $output->writeln($target);
295
        }
296 11
    }
297
298
    /**
299
     * @param InputInterface  $input
300
     * @param OutputInterface $output
301
     * @param string          $file
302
     */
303 11
    private function createFileFromTemplate(InputInterface $input, OutputInterface $output, $file)
304
    {
305 11
        $dryRun = $input->getOption('dry-run');
306
307 11
        $targetFile = basename($file);
308 11
        if (!empty($this->rootDir)) {
309 11
            $targetFile = $this->rootDir.'/'.$targetFile;
310
        }
311
312 11
        $output->writeln($dryRun
313 5
            ? "<info>Would have created file $targetFile</info>"
314 11
            : "<info>Creating file $targetFile</info>"
315
        );
316
317 11
        if ($dryRun) {
318 5
            return;
319
        }
320
321 6
        $this->renderTemplate($input, $output, $file, $targetFile);
322 6
    }
323
324
    /**
325
     * @param InputInterface  $input
326
     * @param OutputInterface $output
327
     * @param string          $file
328
     * @param string          $target
329
     */
330 6
    private function renderTemplate(InputInterface $input, OutputInterface $output, $file, $target)
331
    {
332 6
        if (file_exists($target) && !$input->getOption('force')) {
333 1
            $output->writeln(
334 1
                "<error>$target already exists. Run this command with --force to overwrite</error>"
335
            );
336
        }
337
338 6
        switch ($input->getOption('csspre')) {
339 6
            case 'sass':
340 5
                $stylesheetExtension = 'scss';
341 5
                break;
342 1
            case 'less':
343 1
                $stylesheetExtension = 'less';
344 1
                break;
345
            default:
346
                $stylesheetExtension = 'css';
347
                break;
348
        }
349
350 6
        file_put_contents($target, $this->templating->render("$file.php", [
351 6
            'projectName' => basename(getcwd()),
352 6
            'srcDir' => $input->getOption('src-dir'),
353 6
            'destDir' => $input->getOption('dest-dir'),
354 6
            'prefix' => str_replace('web/', '', $input->getOption('dest-dir')),
355 6
            'coffee' => $input->getOption('coffee'),
356 6
            'cssPre' => $input->getOption('csspre'),
357 6
            'stylesheetExtension' => $stylesheetExtension,
358
        ]));
359 6
    }
360
361
    /**
362
     * @param InputInterface $input
363
     */
364 14
    private function processOptions(InputInterface $input)
365
    {
366 14
        foreach ($input->getOptions() as $name => $value) {
367 14
            if (!$input->isInteractive() && $value === null) {
368 12
                $value = $this->getDefaultOption($name);
369
            }
370
371 14
            if ($value === 'true') {
372 2
                $value = true;
373 14
            } elseif ($value === 'false') {
374 11
                $value = false;
375
            }
376
377 14
            $input->setOption($name, $value);
378
        }
379 14
    }
380
381
    /**
382
     * @param string $name
383
     *
384
     * @return string
385
     */
386 14
    private function getDefaultOption($name)
387
    {
388
        $defaults = [
389 14
            'src-dir' => empty($this->rootDir) ? 'app/Resources' : $this->rootDir.'/app/Resources',
390 14
            'dest-dir' => empty($this->rootDir) ? 'web/assets' : $this->rootDir.'/web/assets',
391 14
            'pipeline' => 'gulp',
392 14
            'csspre' => 'sass',
393 14
            'coffee' => 'false',
394
        ];
395
396 14
        return $defaults[$name];
397
    }
398
}
399