Skip to content

Commit

Permalink
allow minification to be configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
gwleuverink committed Jan 18, 2024
1 parent 2881bf3 commit 59d8c31
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 28 deletions.
14 changes: 13 additions & 1 deletion config/bundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
| loading. Here you can tweak it's internal timout in ms.
|
*/
'import_resolution_timeout' => env('BUNDLE_IMPORT_RESOLUTION_TIMOUT', 500),
'import_resolution_timeout' => env('BUNDLE_IMPORT_RESOLUTION_TIMEOUT', 500),

/*
|--------------------------------------------------------------------------
Expand All @@ -49,6 +49,18 @@
*/
'sourcemaps_enabled' => env('BUNDLE_SOURCEMAPS_ENABLED', false),

/*
|--------------------------------------------------------------------------
| Minification
|--------------------------------------------------------------------------
|
| All code is minified by default. This can make issues harder to debug.
| Using sourcemaps should relieve this issue. But in case you need it;
| Simply disable the minification option to below to stop minifing.
|
*/
'minify' => env('BUNDLE_MINIFY', true),

/*
|--------------------------------------------------------------------------
| Build paths (glob patterns)
Expand Down
4 changes: 3 additions & 1 deletion docs/advanced-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,11 @@ Since Bundle's core is included with the first `<x-import />` that you load you

> We like to explore ways to inject Bundle's core on every page. This way the `_import()` function does not have to be wrapped in a `DOMContentLoaded` listener. Check out our [roadmap](https://laravel-bundle.dev/roadmap.html#roadmap) to see what else we're cooking up.
The import resolution time may be configured milliseconds by updating the config file or via an env variable `BUNDLE_IMPORT_RESOLUTION_TIMEOUT`

## Minification

<!-- TODO -->
All code is minified by default. This can make issues harder to debug at times. Using sourcemaps should relieve this issue. But in case you need it you can disable minification by updating the config file or via an env variable `BUNDLE_MINIFICATION`.

## Sourcemaps

Expand Down
4 changes: 4 additions & 0 deletions src/BundleManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public function bundle(string $script): SplFileInfo
try {
$processed = $this->bundler->build(
sourcemaps: $this->config()->get('sourcemaps_enabled'),
minify: $this->config()->get('minify'),
inputPath: $this->tempDisk()->path(''),
outputPath: $this->buildDisk()->path(''),
fileName: $file,
Expand Down Expand Up @@ -128,6 +129,9 @@ public static function fake(): MockInterface
{
$mock = Mockery::mock(BundleManagerContract::class, fn ($mock) => $mock
->makePartial()
->shouldReceive('config')
->andReturn(new ConfigRepository([]))

->shouldReceive('bundle')
->andReturn(new SplFileInfo(base_path('composer.json'))) // Just a file we know exists. It won't be touched
->atLeast()->once()
Expand Down
18 changes: 13 additions & 5 deletions src/Bundlers/Bun.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,33 @@ class Bun implements Bundler
{
use Constructable;

public function build(string $inputPath, string $outputPath, string $fileName, bool $sourcemaps = false): SplFileInfo
{
public function build(
string $inputPath,
string $outputPath,
string $fileName,
bool $sourcemaps = false,
bool $minify = true
): SplFileInfo {

$path = base_path('node_modules/.bin/');
$options = [
// '--tsconfig-override' => base_path('jsconfig.json'), // Disable enforcing this. custom config is optional.
// '--splitting', // Breaks relative paths to imports from resources/js (TODO: Experiment more after writing tests)
'--chunk-naming' => 'chunks/[name]-[hash].[ext]', // Not in use without --splitting
'--asset-naming' => 'assets/[name]-[hash].[ext]', // Not in use without --splitting
'--entrypoints' => $inputPath . $fileName,
'--public-path' => $outputPath,
'--outdir' => $outputPath,
'--target' => 'browser',
'--root' => $inputPath,
// '--splitting', // Breaks relative paths to imports from resources/js (TODO: Experiment more after writing tests)
'--format' => 'esm',
'--minify', // Only in production?

$sourcemaps
? '--sourcemap=external'
: '--sourcemap=none',

$minify
? '--minify'
: '',
];

Process::run("{$path}bun build {$this->args($options)}")
Expand Down
35 changes: 21 additions & 14 deletions src/Components/Import.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,15 @@
use Illuminate\View\Component;
use Leuverink\Bundle\BundleManager;
use Leuverink\Bundle\Exceptions\BundlingFailedException;
use Leuverink\Bundle\Contracts\BundleManager as BundleManagerContract;

class Import extends Component
{
public BundleManager $manager;

public function __construct(
public string $module,
public ?string $as = null,
public bool $inline = false
) {
$this->manager = BundleManager::new();
}

public function render()
Expand All @@ -27,6 +25,24 @@ public function render()
}
}

/** Builds the core JavaScript & packages it up in a bundle */
protected function bundle()
{
$js = $this->core();

// Render script tag with bundled code
return view('x-import::script', [
'bundle' => $this->manager()->bundle($js),
]);
}

/** Get an instance of the BundleManager */
protected function manager(): BundleManagerContract
{
return BundleManager::new();
}

/** Determines wherether to raise a console error or throw a PHP exception */
protected function raiseConsoleErrorOrException(BundlingFailedException $e)
{
if (app()->hasDebugModeEnabled()) {
Expand All @@ -42,19 +58,10 @@ protected function raiseConsoleErrorOrException(BundlingFailedException $e)
HTML;
}

protected function bundle()
{
$js = $this->core();

// Render script tag with bundled code
return view('x-import::script', [
'bundle' => $this->manager->bundle($js),
]);
}

/** Builds Bundle's core JavaScript */
protected function core(): string
{
$timeout = $this->manager->config()->get('import_resolution_timeout');
$timeout = $this->manager()->config()->get('import_resolution_timeout');

return <<< JS
// First make sure window.x_import_modules exists
Expand Down
8 changes: 7 additions & 1 deletion src/Contracts/Bundler.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,11 @@

interface Bundler
{
public function build(string $inputPath, string $outputPath, string $fileName, bool $sourcemaps = false): SplFileInfo;
public function build(
string $inputPath,
string $outputPath,
string $fileName,
bool $sourcemaps = false,
bool $minify = true
): SplFileInfo;
}
13 changes: 7 additions & 6 deletions tests/Browser/AlpineIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
class AlpineIntegrationTest extends DuskTestCase
{
/** @test */
public function it_can_bootstrap_alpine_via_import()
public function it_can_bootstrap_alpine_via_iife_import()
{
$browser = $this->blade(<<< 'HTML'
<x-import module="~/bootstrap/alpine" />
Expand All @@ -31,7 +31,7 @@ public function it_can_bootstrap_alpine_via_import()
}

/** @test */
public function it_can_bootstrap_plugins_via_import()
public function it_can_bootstrap_plugins_via_iife_import()
{
$browser = $this->blade(<<< 'HTML'
<x-import module="~/bootstrap/alpine" />
Expand Down Expand Up @@ -74,12 +74,12 @@ public function it_can_use_other_imports_inside_x_init_directive()
$el.innerHTML = filtered[0].name
"
></div>
HTML);
HTML)->pause(20);

// Doesn't raise console errors
$this->assertEmpty($browser->driver->manage()->getLog('browser'));

$browser->assertSee('Fello World!');
$browser->assertSeeIn('#component', 'Fello World!');

}

Expand All @@ -92,6 +92,7 @@ public function it_can_use_other_imports_inside_x_data_directive()
<x-import module="lodash/filter" as="filter" />
<div
id="component"
x-data="{
async init() {
const filter = await _import('filter');
Expand All @@ -108,11 +109,11 @@ public function it_can_use_other_imports_inside_x_data_directive()
}
}"
></div>
HTML);
HTML)->pause(20);

// Doesn't raise console errors
$this->assertEmpty($browser->driver->manage()->getLog('browser'));

$browser->assertSee('Gello World!');
$browser->assertSeeIn('#component', 'Gello World!');
}
}
22 changes: 22 additions & 0 deletions tests/Feature/IntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,28 @@
->toContain('type="module"');
});

// Easiest way to verify minification is to check if the line count is below a certain threshold
it('minifies code when minification enabled', function () {
$lineThreshold = 10;
config()->set('bundle.minify', true);

$script = Blade::renderComponent(new Import('~/output-to-id', 'foo', inline: true));

expect(substr_count($script, "\n"))
->toBeLessThan($lineThreshold);
});

// Easiest way to verify minification is to check if the line count is above a certain threshold
it('doesnt minify code when minification disabled', function () {
$lineThreshold = 10;
config()->set('bundle.minify', false);

$script = Blade::renderComponent(new Import('~/output-to-id', 'foo', inline: true));

expect(substr_count($script, "\n"))
->toBeGreaterThan($lineThreshold);
});

it('serves bundles over http', function () {
$js = <<< 'JS'
const filter = await import('~/output-to-id')
Expand Down

0 comments on commit 59d8c31

Please sign in to comment.