Merge branch 'master' of github.com:roots/sage

This commit is contained in:
Dave Kiss
2017-06-13 08:39:27 -04:00
80 changed files with 779 additions and 358 deletions

View File

@@ -0,0 +1,41 @@
<?php
namespace Roots\Sage\Assets;
/**
* Class JsonManifest
* @package Roots\Sage
* @author QWp6t
*/
class JsonManifest implements ManifestInterface
{
/** @var array */
public $manifest;
/** @var string */
public $dist;
/**
* JsonManifest constructor
*
* @param string $manifestPath Local filesystem path to JSON-encoded manifest
* @param string $distUri Remote URI to assets root
*/
public function __construct($manifestPath, $distUri)
{
$this->manifest = file_exists($manifestPath) ? json_decode(file_get_contents($manifestPath), true) : [];
$this->dist = $distUri;
}
/** @inheritdoc */
public function get($asset)
{
return isset($this->manifest[$asset]) ? $this->manifest[$asset] : $asset;
}
/** @inheritdoc */
public function getUri($asset)
{
return "{$this->dist}/{$this->get($asset)}";
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Roots\Sage\Assets;
/**
* Interface ManifestInterface
* @package Roots\Sage
* @author QWp6t
*/
interface ManifestInterface
{
/**
* Get the cache-busted filename
*
* If the manifest does not have an entry for $asset, then return $asset
*
* @param string $asset The original name of the file before cache-busting
* @return string
*/
public function get($asset);
/**
* Get the cache-busted URI
*
* If the manifest does not have an entry for $asset, then return URI for $asset
*
* @param string $asset The original name of the file before cache-busting
* @return string
*/
public function getUri($asset);
}

8
app/lib/Sage/Config.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
namespace Roots\Sage;
class Config extends \Illuminate\Config\Repository
{
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Roots\Sage;
use Illuminate\Container\Container as BaseContainer;
class Container extends BaseContainer
{
}

View File

@@ -0,0 +1,133 @@
<?php
namespace Roots\Sage;
use Composer\Script\Event;
class PostCreateProject
{
public static function updateHeaders(Event $event)
{
// @codingStandardsIgnoreStart
$io = $event->getIO();
if ($io->isInteractive()) {
$io->write('<info>Define theme headers. Press enter key for default.</info>');
$theme_headers_default = [
'name' => 'Sage Starter Theme',
'uri' => 'https://roots.io/sage/',
'description' => 'Sage is a WordPress starter theme.',
'version' => '9.0.0-beta.3',
'author' => 'Roots',
'author_uri' => 'https://roots.io/'
];
$theme_headers = [
'name' => $io->ask('<info>Theme Name [<comment>'.$theme_headers_default['name'].'</comment>]:</info> ', $theme_headers_default['name']),
'uri' => $io->ask('<info>Theme URI [<comment>'.$theme_headers_default['uri'].'</comment>]:</info> ', $theme_headers_default['uri']),
'description' => $io->ask('<info>Theme Description [<comment>'.$theme_headers_default['description'].'</comment>]:</info> ', $theme_headers_default['description']),
'version' => $io->ask('<info>Theme Version [<comment>'.$theme_headers_default['version'].'</comment>]:</info> ', $theme_headers_default['version']),
'author' => $io->ask('<info>Theme Author [<comment>'.$theme_headers_default['author'].'</comment>]:</info> ', $theme_headers_default['author']),
'author_uri' => $io->ask('<info>Theme Author URI [<comment>'.$theme_headers_default['author_uri'].'</comment>]:</info> ', $theme_headers_default['author_uri'])
];
file_put_contents('resources/style.css', str_replace($theme_headers_default, $theme_headers, file_get_contents('resources/style.css')));
}
}
public static function selectFramework(Event $event)
{
$io = $event->getIO();
$default_framework_pattern = '"bootstrap": ".*"';
$files_to_clear = [
'resources/assets/styles/components/_comments.scss',
'resources/assets/styles/components/_forms.scss',
'resources/assets/styles/components/_wp-classes.scss',
'resources/assets/styles/layouts/_header.scss',
];
if ($io->isInteractive()) {
$frameworks = [
'Bootstrap',
'Foundation',
'Tachyons',
'None'
];
$framework = $io->select('<info>Select a CSS framework</info> <comment>(Default: Bootstrap)</comment>', $frameworks, 0);
switch($framework) {
case 0:
break;
case 1:
file_put_contents('package.json', preg_replace("/{$default_framework_pattern}/", '"foundation-sites": "6.3.0"', file_get_contents('package.json')));
file_put_contents('resources/assets/styles/main.scss', str_replace('@import "~bootstrap/scss/bootstrap";' . "\n", '@import "~foundation-sites/scss/foundation";' . "\n" . '@include foundation-everything;' . "\n", file_get_contents('resources/assets/styles/main.scss')));
file_put_contents('resources/assets/scripts/main.js', str_replace("import 'bootstrap';\n", "import 'foundation-sites/dist/js/foundation';\n", file_get_contents('resources/assets/scripts/main.js')));
foreach($files_to_clear as $file) {
file_put_contents($file, '');
}
break;
case 2:
file_put_contents('package.json', preg_replace("/{$default_framework_pattern}/", '"tachyons-sass": "^4.7.1"', file_get_contents('package.json')));
file_put_contents('assets/styles/main.scss', str_replace('@import "~bootstrap/scss/bootstrap";' . "\n", '@import "~tachyons-sass/tachyons";' . "\n", file_get_contents('assets/styles/main.scss')));
file_put_contents('assets/scripts/main.js', str_replace("import 'bootstrap';\n", '', file_get_contents('assets/scripts/main.js')));
foreach($files_to_clear as $file) {
file_put_contents($file, '');
}
break;
case 3:
file_put_contents('package.json', preg_replace("/\s+{$default_framework_pattern},/", '', file_get_contents('package.json')));
file_put_contents('resources/assets/styles/main.scss', str_replace('@import "~bootstrap/scss/bootstrap";' . "\n", '', file_get_contents('resources/assets/styles/main.scss')));
file_put_contents('resources/assets/scripts/main.js', str_replace("import 'bootstrap';\n", '', file_get_contents('resources/assets/scripts/main.js')));
foreach($files_to_clear as $file) {
file_put_contents($file, '');
}
break;
}
}
}
public static function addFontAwesome(Event $event)
{
$io = $event->getIO();
if ($io->isInteractive()) {
if ($io->askConfirmation('<info>Add Font Awesome?</info> [<comment>y,N</comment>]? ', false)) {
$package = json_decode(file_get_contents('package.json'), true);
$dependencies = $package['dependencies'];
$dependencies = array_merge($dependencies, ['font-awesome' => '^4.7.0']);
$package['dependencies'] = $dependencies;
$package = str_replace(' ', ' ', json_encode($package, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n");
file_put_contents('package.json', $package);
$import_dep_str = '// Import npm dependencies' . "\n";
file_put_contents('resources/assets/styles/main.scss', str_replace($import_dep_str, $import_dep_str . '@import "~font-awesome/scss/font-awesome";' . "\n", file_get_contents('resources/assets/styles/main.scss')));
file_put_contents('resources/assets/styles/common/_variables.scss', "\n" . '$fa-font-path: \'~font-awesome/fonts\';' . "\n", FILE_APPEND);
}
}
}
public static function buildOptions(Event $event)
{
$io = $event->getIO();
if ($io->isInteractive()) {
$io->write('<info>Configure build settings. Press enter key for default.</info>');
$browsersync_settings_default = [
'publicPath' => '/app/themes/'.basename(getcwd()),
'devUrl' => 'http://example.dev'
];
$browsersync_settings = [
'publicPath' => $io->ask('<info>Path to theme directory (eg. /wp-content/themes/sage) [<comment>'.$browsersync_settings_default['publicPath'].'</comment>]:</info> ', $browsersync_settings_default['publicPath']),
'devUrl' => $io->ask('<info>Local development URL of WP site [<comment>'.$browsersync_settings_default['devUrl'].'</comment>]:</info> ', $browsersync_settings_default['devUrl'])
];
file_put_contents('resources/assets/config.json', str_replace('/app/themes/sage', $browsersync_settings['publicPath'], file_get_contents('resources/assets/config.json')));
file_put_contents('resources/assets/config.json', str_replace($browsersync_settings_default['devUrl'], $browsersync_settings['devUrl'], file_get_contents('resources/assets/config.json')));
}
}
// @codingStandardsIgnoreEnd
}

View File

@@ -0,0 +1,129 @@
<?php
namespace Roots\Sage\Template;
use Illuminate\Contracts\Container\Container as ContainerContract;
use Illuminate\Contracts\View\Factory as FactoryContract;
use Illuminate\View\Engines\CompilerEngine;
use Illuminate\View\Engines\EngineInterface;
use Illuminate\View\ViewFinderInterface;
/**
* Class BladeProvider
* @method \Illuminate\View\View file($file, $data = [], $mergeData = [])
* @method \Illuminate\View\View make($file, $data = [], $mergeData = [])
*/
class Blade
{
/** @var ContainerContract */
protected $app;
public function __construct(FactoryContract $env, ContainerContract $app)
{
$this->env = $env;
$this->app = $app;
}
/**
* Get the compiler
*
* @return \Illuminate\View\Compilers\BladeCompiler
*/
public function compiler()
{
static $engineResolver;
if (!$engineResolver) {
$engineResolver = $this->app->make('view.engine.resolver');
}
return $engineResolver->resolve('blade')->getCompiler();
}
/**
* @param string $view
* @param array $data
* @param array $mergeData
* @return string
*/
public function render($view, $data = [], $mergeData = [])
{
/** @var \Illuminate\Contracts\Filesystem\Filesystem $filesystem */
$filesystem = $this->app['files'];
return $this->{$filesystem->exists($view) ? 'file' : 'make'}($view, $data, $mergeData)->render();
}
/**
* @param string $file
* @param array $data
* @param array $mergeData
* @return string
*/
public function compiledPath($file, $data = [], $mergeData = [])
{
$rendered = $this->file($file, $data, $mergeData);
/** @var EngineInterface $engine */
$engine = $rendered->getEngine();
if (!($engine instanceof CompilerEngine)) {
// Using PhpEngine, so just return the file
return $file;
}
$compiler = $engine->getCompiler();
$compiledPath = $compiler->getCompiledPath($rendered->getPath());
if ($compiler->isExpired($compiledPath)) {
$compiler->compile($file);
}
return $compiledPath;
}
/**
* @param string $file
* @return string
*/
public function normalizeViewPath($file)
{
// Convert `\` to `/`
$view = str_replace('\\', '/', $file);
// Add namespace to path if necessary
$view = $this->applyNamespaceToPath($view);
// Remove unnecessary parts of the path
$view = str_replace(array_merge($this->app['config']['view.paths'], ['.blade.php', '.php']), '', $view);
// Remove superfluous and leading slashes
return ltrim(preg_replace('%//+%', '/', $view), '/');
}
/**
* Convert path to view namespace
* @param string $path
* @return string
*/
public function applyNamespaceToPath($path)
{
/** @var ViewFinderInterface $finder */
$finder = $this->app['view.finder'];
if (!method_exists($finder, 'getHints')) {
return $path;
}
$delimiter = $finder::HINT_PATH_DELIMITER;
$hints = $finder->getHints();
$view = array_reduce(array_keys($hints), function ($view, $namespace) use ($delimiter, $hints) {
return str_replace($hints[$namespace], $namespace.$delimiter, $view);
}, $path);
return preg_replace("%{$delimiter}[\\/]*%", $delimiter, $view);
}
/**
* Pass any method to the view Factory instance.
*
* @param string $method
* @param array $params
* @return mixed
*/
public function __call($method, $params)
{
return call_user_func_array([$this->env, $method], $params);
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace Roots\Sage\Template;
use Illuminate\Container\Container;
use Illuminate\Contracts\Container\Container as ContainerContract;
use Illuminate\Events\Dispatcher;
use Illuminate\Filesystem\Filesystem;
use Illuminate\View\ViewServiceProvider;
/**
* Class BladeProvider
*/
class BladeProvider extends ViewServiceProvider
{
/**
* @param ContainerContract $container
* @param array $config
* @SuppressWarnings(PHPMD.StaticAccess)
*/
public function __construct(ContainerContract $container = null, $config = [])
{
/** @noinspection PhpParamsInspection */
parent::__construct($container ?: Container::getInstance());
$this->app->bindIf('config', function () use ($config) {
return $config;
}, true);
}
/**
* Bind required instances for the service provider.
*/
public function register()
{
$this->registerFilesystem();
$this->registerEvents();
$this->registerEngineResolver();
$this->registerViewFinder();
$this->registerFactory();
return $this;
}
/**
* Register Filesystem
*/
public function registerFilesystem()
{
$this->app->bindIf('files', Filesystem::class, true);
return $this;
}
/**
* Register the events dispatcher
*/
public function registerEvents()
{
$this->app->bindIf('events', Dispatcher::class, true);
return $this;
}
/** @inheritdoc */
public function registerEngineResolver()
{
parent::registerEngineResolver();
return $this;
}
/** @inheritdoc */
public function registerFactory()
{
parent::registerFactory();
return $this;
}
/**
* Register the view finder implementation.
*/
public function registerViewFinder()
{
$this->app->bindIf('view.finder', function ($app) {
$config = $this->app['config'];
$paths = $config['view.paths'];
$namespaces = $config['view.namespaces'];
$finder = new FileViewFinder($app['files'], $paths);
array_map([$finder, 'addNamespace'], array_keys($namespaces), $namespaces);
return $finder;
}, true);
return $this;
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace Roots\Sage\Template;
class FileViewFinder extends \Illuminate\View\FileViewFinder
{
const FALLBACK_PARTS_DELIMITER = '-';
/**
* Get an array of possible view files from a single file name.
*
* @param string $name
* @return array
*/
public function getPossibleViewFiles($name)
{
$parts = explode(self::FALLBACK_PARTS_DELIMITER, $name);
$templates[] = array_shift($parts);
foreach ($parts as $i => $part) {
$templates[] = $templates[$i].self::FALLBACK_PARTS_DELIMITER.$part;
}
rsort($templates);
return $this->getPossibleViewFilesFromTemplates($templates);
}
/**
* Get an array of possible view files from an array of templates
*
* @param array $templates
* @return array
*/
public function getPossibleViewFilesFromTemplates($templates)
{
return call_user_func_array('array_merge', array_map(function ($template) {
return array_map(function ($extension) use ($template) {
return str_replace('.', '/', $template).'.'.$extension;
}, $this->extensions);
}, $templates));
}
}