Skip to content

Commit

Permalink
add first-class alpine support! 🚀
Browse files Browse the repository at this point in the history
  • Loading branch information
gwleuverink committed Jan 18, 2024
1 parent ae6f279 commit 96e545d
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 24 deletions.
66 changes: 54 additions & 12 deletions src/Components/Import.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,28 +41,70 @@ protected function raiseConsoleErrorOrException(BundlingFailedException $e)

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

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

protected function core(): string
{
return <<< JS
// First make sure window.x_import_modules exists
if(!window.x_import_modules) window.x_import_modules = {}
// Assign the import to the window.x_import_modules object (or invoke IIFE)
'{$this->as}'
? window.x_import_modules['{$this->as}'] = import('{$this->module}') // Assign it under an alias
: import('{$this->module}') // Only import it (for IIFE no alias needed)
// Assign it under an alias
? window.x_import_modules['{$this->as}'] = import('{$this->module}')
// Only import it (for IIFE no alias needed)
: import('{$this->module}')
// Then we expose a _import function that can retrieve the module as a Promise
// Expose _import function
window._import = async function(alias, exportName = 'default') {
let module = await window.x_import_modules[alias]
// Wait for module to become available (Needed for Alpine support)
const module = await poll(
() => window.x_import_modules[alias],
1000,
5
)
if(module === undefined) {
console.info('When invoking _import() from a script tag make sure it has type="module"')
throw `BUNDLE ERROR: '\${alias}' not found`;
}
return module[exportName] !== undefined
? module[exportName] // Return export if it exists
: module // Otherwise the entire module
// Return export if it exists
? module[exportName]
// Otherwise the entire module
: module
}
JS;
// Render script tag with bundled code
return view('x-import::script', [
'bundle' => BundleManager::new()->bundle($js),
]);
// Import polling helper
async function poll(success, maxDuration, interval) {
const startTime = new Date().getTime();
while (true) {
// If the success callable returns something truthy, return
let result = success()
if (result) return result;
// Check if maxDuration has elapsed
const elapsedTime = new Date().getTime() - startTime;
if (elapsedTime >= maxDuration) {
console.info(`Unable to resolve '\${alias}'. Operation timed out.`)
throw `BUNDLE TIMEOUT: '\${alias}' could not be resolved`;
}
// Wait for a set interval
await new Promise(resolve => setTimeout(resolve, interval));
}
}
JS;
}
}
2 changes: 1 addition & 1 deletion src/Components/views/script.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
{!! file_get_contents($bundle) !!}
</script>
@else
<script src="{{ route('bundle:import', $bundle->getFilename(), false) }}" data-bundle="{{ $module }}" {{ $attributes }}></script>
<script src="{{ route('bundle:import', $bundle->getFilename(), false) }}" data-bundle="{{ $module }}" type="module" {{ $attributes }}></script>
@endif
<!--[ENDBUNDLE]>-->
@else {{-- @once else clause --}}
Expand Down
20 changes: 10 additions & 10 deletions tests/Browser/AlpineIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ public function it_can_use_other_imports_inside_x_init_directive()
<div
id="component"
x-init="
const filter = await _import('lodash', 'filter');
const filter = await _import('filter');
let data = [
{ 'name': 'Foo', 'active': false },
{ 'name': 'Hello World!', 'active': true }
{ 'name': 'Fello World!', 'active': true }
];
// Filter only active
Expand All @@ -79,40 +79,40 @@ public function it_can_use_other_imports_inside_x_init_directive()
// Doesn't raise console errors
$this->assertEmpty($browser->driver->manage()->getLog('browser'));

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

}

/** @test */
public function it_can_use_other_imports_inside_x_data_directive()
{
$browser = $this->blade(<<< 'HTML'
<x-import module="~/bootstrap/alpine" />
<x-import module="lodash/filter" as="filter" />
<div
id="component"
x-data="
async init() {
const filter = await _import('lodash', 'filter');
x-data="{
async init() {
const filter = await _import('filter');
let data = [
{ 'name': 'Foo', 'active': false },
{ 'name': 'Hello World!', 'active': true }
{ 'name': 'Gello World!', 'active': true }
];
// Filter only active
let filtered = filter(data, o => o.active)
$el.innerHTML = filtered[0].name
}
"
}"
></div>
HTML);

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

$browser->assertSeeIn('#component', 'Hello World!');
$browser->assertSee('Gello World!');
}
}
2 changes: 1 addition & 1 deletion tests/Browser/ComponentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public function it_doesnt_render_script_inline_by_default()
}

/** @test */
public function it_works_when_a_iife_is_combined_with_at_least_two_other_imports()
public function it_works_when_a_iife_is_combined_with_multiple_aliased_imports()
{
$browser = $this->blade(<<< 'HTML'
<x-import module="~/function-is-evaluated" />
Expand Down

0 comments on commit 96e545d

Please sign in to comment.