From 0c8b04d5a84a724eaa9228051ab4e030693d2ee9 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sat, 29 Jan 2011 16:10:05 -0800 Subject: [PATCH] Add packaging to Celerity. --- scripts/celerity_mapper.php | 64 ++++++++- src/__celerity_resource_map__.php | 132 ++++++++++++++---- ...AphrontDefaultApplicationConfiguration.php | 2 +- .../controller/CelerityResourceController.php | 31 +++- .../celerity/map/CelerityResourceMap.php | 43 +++++- .../CelerityStaticResourceResponse.php | 8 +- 6 files changed, 241 insertions(+), 39 deletions(-) diff --git a/scripts/celerity_mapper.php b/scripts/celerity_mapper.php index a7a388c087..d281da41ce 100755 --- a/scripts/celerity_mapper.php +++ b/scripts/celerity_mapper.php @@ -1,6 +1,31 @@ #!/usr/bin/env php array( + 'phabricator-core-css', + 'phabricator-core-buttons-css', + 'phabricator-standard-page-view', + 'aphront-dialog-view-css', + 'aphront-form-view-css', + 'aphront-panel-view-css', + 'aphront-side-nav-view-css', + 'aphront-table-view-css', + 'aphront-tokenizer-control-css', + 'aphront-typeahead-control-css', + + 'phabricator-directory-css', + ), + 'differential.pkg.css' => array( + 'differential-core-view-css', + 'differential-changeset-view-css', + 'differential-revision-detail-css', + 'differential-revision-history-css', + 'differential-table-of-contents-css', + ), +); + + require_once dirname(__FILE__).'/__init_script__.php'; if ($argc != 2) { @@ -39,6 +64,8 @@ echo "\n"; $runtime_map = array(); +$hash_map = array(); + $parser = new PhutilDocblockParser(); foreach ($file_map as $path => $info) { $data = Filesystem::readFile($info['disk']); @@ -69,19 +96,50 @@ foreach ($file_map as $path => $info) { $type = 'css'; } - $path = '/res/'.substr($info['hash'], 0, 8).$path; + $uri = '/res/'.substr($info['hash'], 0, 8).$path; + + $hash_map[$provides] = $info['hash']; $runtime_map[$provides] = array( - 'path' => $path, + 'uri' => $uri, 'type' => $type, 'requires' => $requires, + 'disk' => $path, ); } +$package_map = array(); +foreach ($package_spec as $name => $package) { + $hashes = array(); + foreach ($package as $symbol) { + if (empty($hash_map[$symbol])) { + throw new Exception( + "Package specification for '{$name}' includes '{$symbol}', but that ". + "symbol is not defined anywhere."); + } + $hashes[] = $symbol.':'.$hash_map[$symbol]; + } + $key = substr(md5(implode("\n", $hashes)), 0, 8); + $package_map['packages'][$key] = array( + 'name' => $name, + 'symbols' => $package, + 'uri' => '/res/pkg/'.$key.'/'.$name, + 'type' => 'css', // TODO LOL + ); + foreach ($package as $symbol) { + $package_map['reverse'][$symbol] = $key; + } +} + + $runtime_map = var_export($runtime_map, true); $runtime_map = preg_replace('/\s+$/m', '', $runtime_map); $runtime_map = preg_replace('/array \(/', 'array(', $runtime_map); +$package_map = var_export($package_map, true); +$pacakge_map = preg_replace('/\s+$/m', '', $package_map); +$package_map = preg_replace('/array \(/', 'array(', $package_map); + $resource_map = << array( - 'path' => '/res/771b987d/rsrc/css/aphront/dialog-view.css', + 'uri' => '/res/771b987d/rsrc/css/aphront/dialog-view.css', 'type' => 'css', 'requires' => array( ), + 'disk' => '/rsrc/css/aphront/dialog-view.css', ), 'aphront-form-view-css' => array( - 'path' => '/res/785ac1c6/rsrc/css/aphront/form-view.css', + 'uri' => '/res/785ac1c6/rsrc/css/aphront/form-view.css', 'type' => 'css', 'requires' => array( ), + 'disk' => '/rsrc/css/aphront/form-view.css', ), 'aphront-panel-view-css' => array( - 'path' => '/res/fe62e634/rsrc/css/aphront/panel-view.css', + 'uri' => '/res/fe62e634/rsrc/css/aphront/panel-view.css', 'type' => 'css', 'requires' => array( ), + 'disk' => '/rsrc/css/aphront/panel-view.css', ), 'aphront-side-nav-view-css' => array( - 'path' => '/res/0fc0545c/rsrc/css/aphront/side-nav-view.css', + 'uri' => '/res/0fc0545c/rsrc/css/aphront/side-nav-view.css', 'type' => 'css', 'requires' => array( ), + 'disk' => '/rsrc/css/aphront/side-nav-view.css', ), 'aphront-table-view-css' => array( - 'path' => '/res/52b0191f/rsrc/css/aphront/table-view.css', + 'uri' => '/res/52b0191f/rsrc/css/aphront/table-view.css', 'type' => 'css', 'requires' => array( ), + 'disk' => '/rsrc/css/aphront/table-view.css', ), 'aphront-tokenizer-control-css' => array( - 'path' => '/res/a3d23074/rsrc/css/aphront/tokenizer.css', + 'uri' => '/res/a3d23074/rsrc/css/aphront/tokenizer.css', 'type' => 'css', 'requires' => array( 0 => 'aphront-typeahead-control-css', ), + 'disk' => '/rsrc/css/aphront/tokenizer.css', ), 'aphront-typeahead-control-css' => array( - 'path' => '/res/928df9f0/rsrc/css/aphront/typeahead.css', + 'uri' => '/res/928df9f0/rsrc/css/aphront/typeahead.css', 'type' => 'css', 'requires' => array( ), + 'disk' => '/rsrc/css/aphront/typeahead.css', ), 'phabricator-standard-page-view' => array( - 'path' => '/res/1f93ada7/rsrc/css/application/base/standard-page-view.css', + 'uri' => '/res/1f93ada7/rsrc/css/application/base/standard-page-view.css', 'type' => 'css', 'requires' => array( ), + 'disk' => '/rsrc/css/application/base/standard-page-view.css', ), 'differential-changeset-view-css' => array( - 'path' => '/res/658d181a/rsrc/css/application/differential/changeset-view.css', + 'uri' => '/res/658d181a/rsrc/css/application/differential/changeset-view.css', 'type' => 'css', 'requires' => array( ), + 'disk' => '/rsrc/css/application/differential/changeset-view.css', ), 'differential-core-view-css' => array( - 'path' => '/res/525d1a12/rsrc/css/application/differential/core.css', + 'uri' => '/res/525d1a12/rsrc/css/application/differential/core.css', 'type' => 'css', 'requires' => array( ), + 'disk' => '/rsrc/css/application/differential/core.css', ), 'differential-revision-detail-css' => array( - 'path' => '/res/11a36dad/rsrc/css/application/differential/revision-detail.css', + 'uri' => '/res/230a67c6/rsrc/css/application/differential/revision-detail.css', 'type' => 'css', 'requires' => array( ), + 'disk' => '/rsrc/css/application/differential/revision-detail.css', ), 'differential-revision-history-css' => array( - 'path' => '/res/755f3da3/rsrc/css/application/differential/revision-history.css', + 'uri' => '/res/755f3da3/rsrc/css/application/differential/revision-history.css', 'type' => 'css', 'requires' => array( ), + 'disk' => '/rsrc/css/application/differential/revision-history.css', ), 'differential-table-of-contents-css' => array( - 'path' => '/res/a4a7b2b5/rsrc/css/application/differential/table-of-contents.css', + 'uri' => '/res/a4a7b2b5/rsrc/css/application/differential/table-of-contents.css', 'type' => 'css', 'requires' => array( ), + 'disk' => '/rsrc/css/application/differential/table-of-contents.css', ), 'phabricator-directory-css' => array( - 'path' => '/res/6a000601/rsrc/css/application/directory/phabricator-directory.css', + 'uri' => '/res/6a000601/rsrc/css/application/directory/phabricator-directory.css', 'type' => 'css', 'requires' => array( ), + 'disk' => '/rsrc/css/application/directory/phabricator-directory.css', ), 'phabricator-core-buttons-css' => array( - 'path' => '/res/6e348ba4/rsrc/css/core/buttons.css', + 'uri' => '/res/6e348ba4/rsrc/css/core/buttons.css', 'type' => 'css', 'requires' => array( ), + 'disk' => '/rsrc/css/core/buttons.css', ), 'phabricator-core-css' => array( - 'path' => '/res/39ce37c2/rsrc/css/core/core.css', + 'uri' => '/res/39ce37c2/rsrc/css/core/core.css', 'type' => 'css', 'requires' => array( ), + 'disk' => '/rsrc/css/core/core.css', ), 'syntax-highlighting-css' => array( - 'path' => '/res/fb673ece/rsrc/css/core/syntax.css', + 'uri' => '/res/fb673ece/rsrc/css/core/syntax.css', 'type' => 'css', 'requires' => array( ), + 'disk' => '/rsrc/css/core/syntax.css', ), 'javelin-behavior-aphront-basic-tokenizer' => array( - 'path' => '/res/8317d761/rsrc/js/application/core/behavior-tokenizer.js', + 'uri' => '/res/8317d761/rsrc/js/application/core/behavior-tokenizer.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), + 'disk' => '/rsrc/js/application/core/behavior-tokenizer.js', ), 'javelin-behavior-differential-populate' => array( - 'path' => '/res/9982573c/rsrc/js/application/differential/behavior-populate.js', + 'uri' => '/res/9982573c/rsrc/js/application/differential/behavior-populate.js', 'type' => 'js', 'requires' => array( 0 => 'javelin-lib-dev', ), + 'disk' => '/rsrc/js/application/differential/behavior-populate.js', ), 'javelin-init-dev' => array( - 'path' => '/res/c57a9e89/rsrc/js/javelin/init.dev.js', + 'uri' => '/res/c57a9e89/rsrc/js/javelin/init.dev.js', 'type' => 'js', 'requires' => array( ), + 'disk' => '/rsrc/js/javelin/init.dev.js', ), 'javelin-init-prod' => array( - 'path' => '/res/f0172c54/rsrc/js/javelin/init.min.js', + 'uri' => '/res/f0172c54/rsrc/js/javelin/init.min.js', 'type' => 'js', 'requires' => array( ), + 'disk' => '/rsrc/js/javelin/init.min.js', ), 'javelin-lib-dev' => array( - 'path' => '/res/3e747182/rsrc/js/javelin/javelin.dev.js', + 'uri' => '/res/3e747182/rsrc/js/javelin/javelin.dev.js', 'type' => 'js', 'requires' => array( ), + 'disk' => '/rsrc/js/javelin/javelin.dev.js', ), 'javelin-lib-prod' => array( - 'path' => '/res/9438670e/rsrc/js/javelin/javelin.min.js', + 'uri' => '/res/9438670e/rsrc/js/javelin/javelin.min.js', 'type' => 'js', 'requires' => array( ), + 'disk' => '/rsrc/js/javelin/javelin.min.js', ), 'javelin-typeahead-dev' => array( - 'path' => '/res/c81c0f01/rsrc/js/javelin/typeahead.dev.js', + 'uri' => '/res/c81c0f01/rsrc/js/javelin/typeahead.dev.js', 'type' => 'js', 'requires' => array( ), + 'disk' => '/rsrc/js/javelin/typeahead.dev.js', ), 'javelin-typeahead-prod' => array( - 'path' => '/res/1da2d984/rsrc/js/javelin/typeahead.min.js', + 'uri' => '/res/1da2d984/rsrc/js/javelin/typeahead.min.js', 'type' => 'js', 'requires' => array( ), + 'disk' => '/rsrc/js/javelin/typeahead.min.js', + ), +), array ( + 'packages' => + array ( + '4efe7b58' => + array ( + 'name' => 'core.pkg.css', + 'symbols' => + array ( + 0 => 'phabricator-core-css', + 1 => 'phabricator-core-buttons-css', + 2 => 'phabricator-standard-page-view', + 3 => 'aphront-dialog-view-css', + 4 => 'aphront-form-view-css', + 5 => 'aphront-panel-view-css', + 6 => 'aphront-side-nav-view-css', + 7 => 'aphront-table-view-css', + 8 => 'aphront-tokenizer-control-css', + 9 => 'aphront-typeahead-control-css', + 10 => 'phabricator-directory-css', + ), + 'uri' => '/res/pkg/4efe7b58/core.pkg.css', + 'type' => 'css', + ), + '69b11588' => + array ( + 'name' => 'differential.pkg.css', + 'symbols' => + array ( + 0 => 'differential-core-view-css', + 1 => 'differential-changeset-view-css', + 2 => 'differential-revision-detail-css', + 3 => 'differential-revision-history-css', + 4 => 'differential-table-of-contents-css', + ), + 'uri' => '/res/pkg/69b11588/differential.pkg.css', + 'type' => 'css', + ), + ), + 'reverse' => + array ( + 'phabricator-core-css' => '4efe7b58', + 'phabricator-core-buttons-css' => '4efe7b58', + 'phabricator-standard-page-view' => '4efe7b58', + 'aphront-dialog-view-css' => '4efe7b58', + 'aphront-form-view-css' => '4efe7b58', + 'aphront-panel-view-css' => '4efe7b58', + 'aphront-side-nav-view-css' => '4efe7b58', + 'aphront-table-view-css' => '4efe7b58', + 'aphront-tokenizer-control-css' => '4efe7b58', + 'aphront-typeahead-control-css' => '4efe7b58', + 'phabricator-directory-css' => '4efe7b58', + 'differential-core-view-css' => '69b11588', + 'differential-changeset-view-css' => '69b11588', + 'differential-revision-detail-css' => '69b11588', + 'differential-revision-history-css' => '69b11588', + 'differential-table-of-contents-css' => '69b11588', ), )); diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php index 9f482deb13..a5553423ee 100644 --- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php @@ -89,7 +89,7 @@ class AphrontDefaultApplicationConfiguration ), '/res/' => array( - '(?[a-f0-9]{8})/(?.+\.(?:css|js))$' + '(?pkg/)?(?[a-f0-9]{8})/(?.+\.(?:css|js))$' => 'CelerityResourceController', ), diff --git a/src/infratructure/celerity/controller/CelerityResourceController.php b/src/infratructure/celerity/controller/CelerityResourceController.php index 8634cf2ed4..198a23cc04 100644 --- a/src/infratructure/celerity/controller/CelerityResourceController.php +++ b/src/infratructure/celerity/controller/CelerityResourceController.php @@ -20,10 +20,12 @@ class CelerityResourceController extends AphrontController { private $path; private $hash; + private $package; public function willProcessRequest(array $data) { $this->path = $data['path']; $this->hash = $data['hash']; + $this->package = !empty($data['package']); } public function processRequest() { @@ -35,16 +37,33 @@ class CelerityResourceController extends AphrontController { if (!preg_match('/\.(css|js)$/', $path, $matches)) { throw new Exception("Only CSS and JS resources may be served."); } - + $type = $matches[1]; - $root = dirname(phutil_get_library_root('phabricator')); - try { - $data = Filesystem::readFile($root.'/webroot/'.$path); - } catch (Exception $ex) { - return new Aphront404Response(); + if ($this->package) { + $map = CelerityResourceMap::getInstance(); + $paths = $map->resolvePackage($this->hash); + if (!$paths) { + return new Aphront404Response(); + } + + try { + $data = array(); + foreach ($paths as $path) { + $data[] = Filesystem::readFile($root.'/webroot/'.$path); + } + $data = implode("\n\n", $data); + } catch (Exception $ex) { + return new Aphront404Response(); + } + } else { + try { + $data = Filesystem::readFile($root.'/webroot/'.$path); + } catch (Exception $ex) { + return new Aphront404Response(); + } } $response = new AphrontFileResponse(); diff --git a/src/infratructure/celerity/map/CelerityResourceMap.php b/src/infratructure/celerity/map/CelerityResourceMap.php index ee3ea1ca17..6ea9d8bdb9 100644 --- a/src/infratructure/celerity/map/CelerityResourceMap.php +++ b/src/infratructure/celerity/map/CelerityResourceMap.php @@ -20,6 +20,7 @@ final class CelerityResourceMap { private static $instance; private $resourceMap; + private $packageMap; public static function getInstance() { if (empty(self::$instance)) { @@ -66,10 +67,50 @@ final class CelerityResourceMap { $map[$symbol] = $info; } + + public function setPackageMap($package_map) { + $this->packageMap = $package_map; + } + + public function packageResources(array $resolved_map) { + $packaged = array(); + $handled = array(); + foreach ($resolved_map as $symbol => $info) { + if (isset($handled[$symbol])) { + continue; + } + if (empty($this->packageMap['reverse'][$symbol])) { + $packaged[$symbol] = $info; + } else { + $package = $this->packageMap['reverse'][$symbol]; + $package_info = $this->packageMap['packages'][$package]; + $packaged[$package_info['name']] = $package_info; + foreach ($package_info['symbols'] as $symbol) { + $handled[$symbol] = true; + } + } + } + return $packaged; + } + + public function resolvePackage($package_hash) { + $package = idx($this->packageMap['packages'], $package_hash); + if (!$package) { + return null; + } + + $paths = array(); + foreach ($package['symbols'] as $symbol) { + $paths[] = $this->resourceMap[$symbol]['disk']; + } + + return $paths; + } } -function celerity_register_resource_map(array $map) { +function celerity_register_resource_map(array $map, array $package_map) { $instance = CelerityResourceMap::getInstance(); $instance->setResourceMap($map); + $instance->setPackageMap($package_map); } diff --git a/src/infratructure/celerity/response/CelerityStaticResourceResponse.php b/src/infratructure/celerity/response/CelerityStaticResourceResponse.php index 2207602a8a..c8956afaaa 100644 --- a/src/infratructure/celerity/response/CelerityStaticResourceResponse.php +++ b/src/infratructure/celerity/response/CelerityStaticResourceResponse.php @@ -21,6 +21,7 @@ final class CelerityStaticResourceResponse { private $symbols = array(); private $needsResolve = true; private $resolved; + private $packaged; private $metadata = array(); private $metadataBlock = 0; private $behaviors = array(); @@ -57,6 +58,7 @@ final class CelerityStaticResourceResponse { if ($this->needsResolve) { $map = CelerityResourceMap::getInstance(); $this->resolved = $map->resolveResources(array_keys($this->symbols)); + $this->packaged = $map->packageResources($this->resolved); $this->needsResolve = false; } return $this; @@ -65,7 +67,7 @@ final class CelerityStaticResourceResponse { public function renderResourcesOfType($type) { $this->resolveResources(); $output = array(); - foreach ($this->resolved as $resource) { + foreach ($this->packaged as $resource) { if ($resource['type'] == $type) { $output[] = $this->renderResource($resource); } @@ -76,10 +78,10 @@ final class CelerityStaticResourceResponse { private function renderResource(array $resource) { switch ($resource['type']) { case 'css': - $path = phutil_escape_html($resource['path']); + $path = phutil_escape_html($resource['uri']); return ''; case 'js': - $path = phutil_escape_html($resource['path']); + $path = phutil_escape_html($resource['uri']); return ''; }