Skip to content

Instantly share code, notes, and snippets.

@mblarsen
Last active June 11, 2018 00:43
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mblarsen/b9cddae32ef0a3cfb93f7ed71720fc60 to your computer and use it in GitHub Desktop.
Save mblarsen/b9cddae32ef0a3cfb93f7ed71720fc60 to your computer and use it in GitHub Desktop.
Laravel and Webpack (with webpack-dev-server)
<?php
namespace Supawdog\Console\Commands;
use Illuminate\Console\Command;
use Symfony\Component\Process\ProcessUtils;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Process\PhpExecutableFinder;
class WebpackCommand extends Command
{
protected $signature = 'webpack {--config=webpack.config.js}';
protected $description = 'Serve the application on the PHP development server';
public function fire()
{
chdir($this->laravel->publicPath());
$config_path = $this->option('config');
list($host, $port) = $this->getHostAndPort($config_path);
$this->info("Backend started on http://{$host}:{$port}/ as proxy");
$this->info("Client running on " . $this->getClientUrl($config_path));
$binary = ProcessUtils::escapeArgument((new PhpExecutableFinder)->find(false));
$base = ProcessUtils::escapeArgument($this->laravel->basePath());
passthru("WEBPACK_DEV=running {$binary} -S {$host}:{$port} {$base}/server.php");
}
protected function getClientUrl(string $config_path) : string
{
$config = $this->getConfig($config_path);
return array_get($config, 'output.publicPath');
}
protected function getHostAndPort(string $config_path) : array
{
$config = $this->getConfig($config_path);
$proxies = array_get($config, 'devServer.proxy');
$first_proxy = array_first(array_values($proxies));
$host = parse_url($first_proxy, PHP_URL_HOST);
$port = parse_url($first_proxy, PHP_URL_PORT);
return [$host, $port];
}
protected function getConfig(string $config_path) : array
{
static $config;
if (is_null($config)) {
if (strpos($config_path, '/') !== 0) {
$base = $this->laravel->basePath();
$config_path = "/$base/$config_path";
}
// Run through node to "compile" the config
exec("node -e 'console.log(JSON.stringify(require(\"$config_path\")))'", $json);
$config = json_decode($json[0], true);
}
return $config;
}
}
<?php
/*
* A replacement for elixir() that will work for both typical laravel development where the
* blade file will include links and references to stylesheets and javascript, and it will
* work for when you run laravel as the backend in a proxy setup with webpack-dev-server
* as the controller.
*/
if (! function_exists('webpack')) {
function webpack($resource, $is_entry = false) : string
{
static $manifest;
$is_webpack_dev = !!getenv('WEBPACK_DEV');
$type = dirname($resource);
$chunk = basename($resource);
// If in webpack dev mode only return entries
if ($is_webpack_dev) {
if (! $is_entry) {
return '';
}
return call_user_func("webpack__$type", "$resource.$type");
}
if (is_null($manifest)) {
$manifest = json_decode(file_get_contents(public_path('rev-manifest.json')), true);
}
if (isset($manifest[$chunk])) {
$assets = $manifest[$chunk];
// If only one it is a string
if (is_string($manifest[$chunk])) {
$assets = [$assets];
}
// Match chunk and type
$asset = array_first($assets, function ($asset) use ($resource) {
return strpos($asset, $resource) === 0;
});
return call_user_func("webpack__$type", $asset);
}
throw new InvalidArgumentException("Resource {$resource} is not defined in asset manifest.");
}
function webpack__js($url)
{
return "<script type=\"text/javascript\" src=\"$url\"></script>";
}
function webpack__css($url)
{
return "<link rel=\"stylesheet\" href=\"$url\"/>";
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Supaw Dog') }}</title>
{!! webpack('css/commons') !!} <!-- not marked as entry -->
@stack('styles')
<script>
window.Supawdog = <?php echo json_encode([
'csrfToken' => csrf_token()
]); ?>
</script>
</head>
<body>
<div id="app">
@yield('content')
</div>
@yield('footer')
{!! webpack('js/commons') !!} <!-- not marked as entry -->
@stack('scripts')
</body>
</html>
@extends('layouts.app')
@push('styles')
{!! webpack('css/app') !!} <!-- not marked as entry -->
@endpush
@push('scripts')
{!! webpack('js/app', true) !!} <!-- this is the entry point -->
@endpush
// Vist this URL in browser
config.output.publicPath = 'http://mysite.dev:8080/'
// Proxy to Laravel backend
config.devServer = {
contentBase: 'public',
hot: true,
inline: true,
proxy: {
"**": "http://0.0.0.0:8000/",
}
}
config.plugins = [
... config.plugins,
// ... other production plugins
// Dump manifest assetsByChunkName which is used by the webpack helper function
function onBuildComplete() {
this.plugin('done', function (stats) {
fs.writeFileSync(
path.join(__dirname, 'public', 'rev-manifest.json'),
JSON.stringify(stats.toJson().assetsByChunkName)
)
})
}
@mblarsen
Copy link
Author

You can also use ChunkManifestPlugin and then let require/webpack handle everything.

@Grawl
Copy link

Grawl commented Feb 15, 2017

"**": "http://0.0.0.0:8000/"

finally I found this kind of magic

in Browsersync, it was proxy: env.APP_URL

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment