Skip to content

Commit

Permalink
Allow working with multiple entrypoints (#58)
Browse files Browse the repository at this point in the history
* feat: Change input_css option to accept an array of input css files

* feat: Allow backward compatibility by allowing to accept a string as input css

* fix: Fix "The input CSS file is not one of the configured input files." when css file is provided as relative path to tailwind:build command

* feat: Provide input css file as an optional argument instead of an option to `tailwind:build` command

* fix: Fix failed PHPStan and PHP CS Fixer tests

* fix: Store input files with their absolute path in Tailwind Builder

* fix: Fix inconsistency in `getInputCssPaths` mocked return value.

The mocked return value should use `realpath`

* fix: Revert the documentation to use the `input_css` config option

* docs: Update the documentation to explain the current internal working

* refactor: Rename `tailwind:bundle`'s argument from `input` to `input_css` for consistency with config file

* refactor: Change `getInputCssContent` parameter type from `?string` to `string` as it's always passed
  • Loading branch information
adeys authored Jul 5, 2024
1 parent 11b5a11 commit 4cde58e
Show file tree
Hide file tree
Showing 12 changed files with 109 additions and 43 deletions.
19 changes: 15 additions & 4 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,11 @@ download the correct Tailwind binary for your system into a ``var/tailwind/``
directory.

When you run ``tailwind:build``, that binary is used to compile
your CSS file into a ``var/tailwind/tailwind.built.css`` file. Finally,
when the contents of ``assets/styles/app.css`` is requested, the bundle
swaps the contents of that file with the contents of ``var/tailwind/tailwind.built.css``.
each CSS file into a ``var/tailwind/<filename>.built.css`` file.
Finally, when the contents of the CSS file is requested, the bundle swaps the
contents of that file with the contents of ``var/tailwind/<filename>.built.css``.

E.g. : A request for ``assets/styles/app.css`` will be replaced by ``var/tailwind/app.built.css``.
Nice!

Deploying
Expand Down Expand Up @@ -152,7 +154,7 @@ To see the full config from this bundle, run:
$ php bin/console config:dump symfonycasts_tailwind
The main option is ``input_css`` option, which defaults to ``assets/styles/app.css``.
This represents the "source" Tailwind file (the one that contains the ``@tailwind``
This represents the "source" Tailwind files (the one that contains the ``@tailwind``
directives):

.. code-block:: yaml
Expand All @@ -161,6 +163,15 @@ directives):
symfonycasts_tailwind:
input_css: 'assets/styles/other.css'
It's possible to use multiple input files by providing an array:
.. code-block:: yaml
# config/packages/symfonycasts_tailwind.yaml
symfonycasts_tailwind:
input_css:
- 'assets/styles/other.css'
- 'assets/styles/another.css'
Another option is the ``config_file`` option, which defaults to ``tailwind.config.js``.
This represents the Tailwind configuration file:

Expand Down
2 changes: 1 addition & 1 deletion phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ parameters:
- src
ignoreErrors:
-
message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\:\\:scalarNode\\(\\)\\.$#"
message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeParentInterface\\:\\:beforeNormalization\\(\\)\\.$#"
count: 1
path: src/DependencyInjection/TailwindExtension.php
9 changes: 6 additions & 3 deletions src/AssetMapper/TailwindCssAssetCompiler.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ public function __construct(private TailwindBuilder $tailwindBuilder)

public function supports(MappedAsset $asset): bool
{
return realpath($asset->sourcePath) === realpath($this->tailwindBuilder->getInputCssPath());
return \in_array(
realpath($asset->sourcePath),
$this->tailwindBuilder->getInputCssPaths(),
);
}

public function compile(string $content, MappedAsset $asset, AssetMapperInterface $assetMapper): string
{
$asset->addFileDependency($this->tailwindBuilder->getInternalOutputCssPath());
$asset->addFileDependency($this->tailwindBuilder->getInternalOutputCssPath($asset->sourcePath));

return $this->tailwindBuilder->getOutputCssContent();
return $this->tailwindBuilder->getOutputCssContent($asset->sourcePath);
}
}
3 changes: 3 additions & 0 deletions src/Command/TailwindBuildCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
Expand All @@ -32,6 +33,7 @@ public function __construct(
protected function configure(): void
{
$this
->addArgument('input_css', InputArgument::OPTIONAL, 'The input CSS file to compile')
->addOption('watch', 'w', null, 'Watch for changes and rebuild automatically')
->addOption('poll', null, null, 'Use polling instead of filesystem events when watching')
->addOption('minify', 'm', InputOption::VALUE_NONE, 'Minify the output CSS')
Expand All @@ -47,6 +49,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
watch: $input->getOption('watch'),
poll: $input->getOption('poll'),
minify: $input->getOption('minify'),
inputFile: $input->getArgument('input_css'),
);
$process->wait(function ($type, $buffer) use ($io) {
$io->write($buffer);
Expand Down
2 changes: 1 addition & 1 deletion src/Command/TailwindInitCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ private function createTailwindConfig(SymfonyStyle $io): bool

private function addTailwindDirectives(SymfonyStyle $io): void
{
$inputFile = $this->tailwindBuilder->getInputCssPath();
$inputFile = $this->tailwindBuilder->getInputCssPaths()[0];
$contents = is_file($inputFile) ? file_get_contents($inputFile) : '';
if (str_contains($contents, '@tailwind base')) {
$io->note(sprintf('Tailwind directives already exist in "%s"', $inputFile));
Expand Down
8 changes: 5 additions & 3 deletions src/DependencyInjection/TailwindExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@ public function getConfigTreeBuilder(): TreeBuilder

$rootNode
->children()
->scalarNode('input_css')
->info('Path to CSS file to process through Tailwind')
->defaultValue('%kernel.project_dir%/assets/styles/app.css')
->arrayNode('input_css')
->prototype('scalar')->end()
->beforeNormalization()->castToArray()->end()
->info('Paths to CSS files to process through Tailwind')
->defaultValue(['%kernel.project_dir%/assets/styles/app.css'])
->end()
->scalarNode('config_file')
->info('Path to the tailwind.config.js file')
Expand Down
57 changes: 38 additions & 19 deletions src/TailwindBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,35 +24,39 @@
class TailwindBuilder
{
private ?SymfonyStyle $output = null;
private readonly string $inputPath;
private readonly array $inputPaths;

public function __construct(
private readonly string $projectRootDir,
string $inputPath,
array $inputPaths,
private readonly string $tailwindVarDir,
private CacheInterface $cache,
private readonly ?string $binaryPath = null,
private readonly ?string $binaryVersion = null,
private readonly string $configPath = 'tailwind.config.js'
) {
if (is_file($inputPath)) {
$this->inputPath = $inputPath;
} else {
$this->inputPath = $projectRootDir.'/'.$inputPath;

if (!is_file($this->inputPath)) {
throw new \InvalidArgumentException(sprintf('The input CSS file "%s" does not exist.', $inputPath));
}
$paths = [];
foreach ($inputPaths as $inputPath) {
$paths[] = $this->validateInputFile($inputPath);
}

$this->inputPaths = $paths;
}

public function runBuild(
bool $watch,
bool $poll,
bool $minify,
?string $inputFile = null,
): Process {
$binary = $this->createBinary();
$arguments = ['-c', $this->configPath, '-i', $this->inputPath, '-o', $this->getInternalOutputCssPath()];

$inputPath = $this->validateInputFile($inputFile ?? $this->inputPaths[0]);
if (!\in_array($inputPath, $this->inputPaths)) {
throw new \InvalidArgumentException(sprintf('The input CSS file "%s" is not one of the configured input files.', $inputPath));
}

$arguments = ['-c', $this->configPath, '-i', $inputPath, '-o', $this->getInternalOutputCssPath($inputPath)];
if ($watch) {
$arguments[] = '--watch';
if ($poll) {
Expand Down Expand Up @@ -82,7 +86,7 @@ public function runBuild(
return $process;
}

public function runInit()
public function runInit(): Process
{
$binary = $this->createBinary();
$process = $binary->createProcess(['init']);
Expand All @@ -102,28 +106,43 @@ public function setOutput(SymfonyStyle $output): void
$this->output = $output;
}

public function getInternalOutputCssPath(): string
public function getInternalOutputCssPath(string $inputPath): string
{
return $this->tailwindVarDir.'/tailwind.built.css';
$inputFileName = pathinfo($inputPath, \PATHINFO_FILENAME);

return "{$this->tailwindVarDir}/{$inputFileName}.built.css";
}

public function getInputCssPath(): string
public function getInputCssPaths(): array
{
return $this->inputPath;
return $this->inputPaths;
}

public function getConfigFilePath(): string
{
return $this->configPath;
}

public function getOutputCssContent(): string
public function getOutputCssContent(string $inputFile): string
{
if (!is_file($this->getInternalOutputCssPath())) {
if (!is_file($this->getInternalOutputCssPath($inputFile))) {
throw new \RuntimeException('Built Tailwind CSS file does not exist: run "php bin/console tailwind:build" to generate it');
}

return file_get_contents($this->getInternalOutputCssPath());
return file_get_contents($this->getInternalOutputCssPath($inputFile));
}

private function validateInputFile(string $inputPath): string
{
if (is_file($inputPath)) {
return realpath($inputPath);
}

if (is_file($this->projectRootDir.'/'.$inputPath)) {
return realpath($this->projectRootDir.'/'.$inputPath);
}

throw new \InvalidArgumentException(sprintf('The input CSS file "%s" does not exist.', $inputPath));
}

private function createBinary(): TailwindBinary
Expand Down
4 changes: 2 additions & 2 deletions tests/AssetMapper/TailwindCssAssetCompilerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public function testCompile()
{
$builder = $this->createMock(TailwindBuilder::class);
$builder->expects($this->any())
->method('getInputCssPath')
->willReturn(__DIR__.'/../fixtures/assets/styles/app.css');
->method('getInputCssPaths')
->willReturn([realpath(__DIR__.'/../fixtures/assets/styles/app.css')]);
$builder->expects($this->once())
->method('getInternalOutputCssPath');
$builder->expects($this->once())
Expand Down
6 changes: 3 additions & 3 deletions tests/FunctionalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ protected function setUp(): void
$fs->remove($tailwindVarDir);
}
$fs->mkdir($tailwindVarDir);
file_put_contents($tailwindVarDir.'/tailwind.built.css', <<<EOF
file_put_contents($tailwindVarDir.'/app.built.css', <<<EOF
body {
padding: 17px;
background-image: url('../images/penguin.png');
Expand All @@ -35,8 +35,8 @@ protected function setUp(): void

protected function tearDown(): void
{
if (is_file(__DIR__.'/fixtures/var/tailwind/tailwind.built.css')) {
unlink(__DIR__.'/fixtures/var/tailwind/tailwind.built.css');
if (is_file(__DIR__.'/fixtures/var/tailwind/app.built.css')) {
unlink(__DIR__.'/fixtures/var/tailwind/app.built.css');
}
}

Expand Down
33 changes: 27 additions & 6 deletions tests/TailwindBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public function testIntegrationWithDefaultOptions(): void
{
$builder = new TailwindBuilder(
__DIR__.'/fixtures',
__DIR__.'/fixtures/assets/styles/app.css',
[__DIR__.'/fixtures/assets/styles/app.css'],
__DIR__.'/fixtures/var/tailwind',
new ArrayAdapter(),
null,
Expand All @@ -50,17 +50,17 @@ public function testIntegrationWithDefaultOptions(): void
$process->wait();

$this->assertTrue($process->isSuccessful());
$this->assertFileExists(__DIR__.'/fixtures/var/tailwind/tailwind.built.css');
$this->assertFileExists(__DIR__.'/fixtures/var/tailwind/app.built.css');

$outputFileContents = file_get_contents(__DIR__.'/fixtures/var/tailwind/tailwind.built.css');
$outputFileContents = file_get_contents(__DIR__.'/fixtures/var/tailwind/app.built.css');
$this->assertStringContainsString("body {\n background-color: red;\n}", $outputFileContents, 'The output file should contain non-minified CSS.');
}

public function testIntegrationWithMinify(): void
{
$builder = new TailwindBuilder(
__DIR__.'/fixtures',
__DIR__.'/fixtures/assets/styles/app.css',
[__DIR__.'/fixtures/assets/styles/app.css'],
__DIR__.'/fixtures/var/tailwind',
new ArrayAdapter(),
null,
Expand All @@ -71,9 +71,30 @@ public function testIntegrationWithMinify(): void
$process->wait();

$this->assertTrue($process->isSuccessful());
$this->assertFileExists(__DIR__.'/fixtures/var/tailwind/tailwind.built.css');
$this->assertFileExists(__DIR__.'/fixtures/var/tailwind/app.built.css');

$outputFileContents = file_get_contents(__DIR__.'/fixtures/var/tailwind/tailwind.built.css');
$outputFileContents = file_get_contents(__DIR__.'/fixtures/var/tailwind/app.built.css');
$this->assertStringContainsString('body{background-color:red}', $outputFileContents, 'The output file should contain minified CSS.');
}

public function testBuildProvidedInputFile(): void
{
$builder = new TailwindBuilder(
__DIR__.'/fixtures',
[__DIR__.'/fixtures/assets/styles/app.css', __DIR__.'/fixtures/assets/styles/second.css'],
__DIR__.'/fixtures/var/tailwind',
new ArrayAdapter(),
null,
null,
__DIR__.'/fixtures/tailwind.config.js'
);
$process = $builder->runBuild(watch: false, poll: false, minify: true, inputFile: 'assets/styles/second.css');
$process->wait();

$this->assertTrue($process->isSuccessful());
$this->assertFileExists(__DIR__.'/fixtures/var/tailwind/second.built.css');

$outputFileContents = file_get_contents(__DIR__.'/fixtures/var/tailwind/second.built.css');
$this->assertStringContainsString('body{background-color:blue}', $outputFileContents, 'The output file should contain minified CSS.');
}
}
2 changes: 1 addition & 1 deletion tests/fixtures/TailwindTestKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ protected function configureContainer(ContainerBuilder $container, LoaderInterfa
]);

$container->loadFromExtension('symfonycasts_tailwind', [
'input_css' => __DIR__.'/assets/styles/app.css',
'input_css' => [__DIR__.'/assets/styles/app.css'],
]);
}

Expand Down
7 changes: 7 additions & 0 deletions tests/fixtures/assets/styles/second.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

body {
background-color: blue;
}

0 comments on commit 4cde58e

Please sign in to comment.