mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-28 00:10:57 +01:00
Drive all Celerity operations from the new map
Summary: Ref T4222. - Removes the old map and changes the CelerityResourceMap API to be entirely driven by the new map. - The new map is about 50% smaller and organized more sensibly. - This removes the `/pkg/` URI component. All resources are now required to have unique names, so we can tell if a resource is a package or not by looking at the name. - Removes some junky old APIs. - Cleans up some other APIs. - Added some feedback for `bin/celerity map`. - `CelerityResourceMap` is still a singleton which is inextricably bound to the Phabricator map; this will change in the future. Test Plan: - Reloaded pages. - Verified packaging works by looking at generated includes. - Forced minification on and verified it worked. - Forced no-timestamps on and verified it worked. - Rebuilt map. - Ran old script and verified error message. - Checked logs. Reviewers: btrahan, hach-que Reviewed By: hach-que CC: chad, aran Maniphest Tasks: T4222 Differential Revision: https://secure.phabricator.com/D7872
This commit is contained in:
parent
60cb65bfbb
commit
2c35532256
19 changed files with 2665 additions and 5334 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,6 +1,5 @@
|
|||
.DS_Store
|
||||
._*
|
||||
/webroot/rsrc/custom
|
||||
.#*
|
||||
*#
|
||||
*~
|
||||
|
|
2173
resources/celerity/map.php
Normal file
2173
resources/celerity/map.php
Normal file
File diff suppressed because it is too large
Load diff
180
resources/celerity/packages.php
Normal file
180
resources/celerity/packages.php
Normal file
|
@ -0,0 +1,180 @@
|
|||
<?php
|
||||
|
||||
return array(
|
||||
'javelin.pkg.js' => array(
|
||||
'javelin-util',
|
||||
'javelin-install',
|
||||
'javelin-event',
|
||||
'javelin-stratcom',
|
||||
'javelin-behavior',
|
||||
'javelin-resource',
|
||||
'javelin-request',
|
||||
'javelin-vector',
|
||||
'javelin-dom',
|
||||
'javelin-json',
|
||||
'javelin-uri',
|
||||
'javelin-workflow',
|
||||
'javelin-mask',
|
||||
'javelin-typeahead',
|
||||
'javelin-typeahead-normalizer',
|
||||
'javelin-typeahead-source',
|
||||
'javelin-typeahead-preloaded-source',
|
||||
'javelin-typeahead-ondemand-source',
|
||||
'javelin-tokenizer',
|
||||
'javelin-history',
|
||||
),
|
||||
'core.pkg.js' => array(
|
||||
'javelin-behavior-aphront-basic-tokenizer',
|
||||
'javelin-behavior-workflow',
|
||||
'javelin-behavior-aphront-form-disable-on-submit',
|
||||
'phabricator-keyboard-shortcut-manager',
|
||||
'phabricator-keyboard-shortcut',
|
||||
'javelin-behavior-phabricator-keyboard-shortcuts',
|
||||
'javelin-behavior-refresh-csrf',
|
||||
'javelin-behavior-phabricator-watch-anchor',
|
||||
'javelin-behavior-phabricator-autofocus',
|
||||
'phabricator-menu-item',
|
||||
'phabricator-dropdown-menu',
|
||||
'phabricator-phtize',
|
||||
'javelin-behavior-phabricator-oncopy',
|
||||
'phabricator-tooltip',
|
||||
'javelin-behavior-phabricator-tooltips',
|
||||
'phabricator-prefab',
|
||||
'javelin-behavior-device',
|
||||
'javelin-behavior-toggle-class',
|
||||
'javelin-behavior-lightbox-attachments',
|
||||
'phabricator-busy',
|
||||
'javelin-aphlict',
|
||||
'phabricator-notification',
|
||||
'javelin-behavior-aphlict-listen',
|
||||
'javelin-behavior-phabricator-search-typeahead',
|
||||
'javelin-behavior-konami',
|
||||
'javelin-behavior-aphlict-dropdown',
|
||||
'javelin-behavior-history-install',
|
||||
'javelin-behavior-phabricator-gesture',
|
||||
|
||||
'javelin-behavior-phabricator-active-nav',
|
||||
'javelin-behavior-phabricator-nav',
|
||||
'javelin-behavior-phabricator-remarkup-assist',
|
||||
'phabricator-textareautils',
|
||||
'phabricator-file-upload',
|
||||
'javelin-behavior-global-drag-and-drop',
|
||||
'javelin-behavior-phabricator-reveal-content',
|
||||
'phabricator-hovercard',
|
||||
'javelin-behavior-phabricator-hovercards',
|
||||
'javelin-color',
|
||||
'javelin-fx',
|
||||
),
|
||||
'core.pkg.css' => array(
|
||||
'phabricator-core-css',
|
||||
'phabricator-zindex-css',
|
||||
'phui-button-css',
|
||||
'phabricator-standard-page-view',
|
||||
'aphront-dialog-view-css',
|
||||
'phui-form-view-css',
|
||||
'aphront-panel-view-css',
|
||||
'aphront-table-view-css',
|
||||
'aphront-tokenizer-control-css',
|
||||
'aphront-typeahead-control-css',
|
||||
'aphront-list-filter-view-css',
|
||||
|
||||
'phabricator-jump-nav',
|
||||
|
||||
'phabricator-remarkup-css',
|
||||
'syntax-highlighting-css',
|
||||
'aphront-pager-view-css',
|
||||
'phabricator-transaction-view-css',
|
||||
'aphront-tooltip-css',
|
||||
'phabricator-flag-css',
|
||||
'aphront-error-view-css',
|
||||
|
||||
'sprite-icons-css',
|
||||
'sprite-gradient-css',
|
||||
'sprite-menu-css',
|
||||
'sprite-apps-large-css',
|
||||
'sprite-status-css',
|
||||
|
||||
'phabricator-main-menu-view',
|
||||
'phabricator-notification-css',
|
||||
'phabricator-notification-menu-css',
|
||||
'lightbox-attachment-css',
|
||||
'phui-header-view-css',
|
||||
'phabricator-filetree-view-css',
|
||||
'phabricator-nav-view-css',
|
||||
'phabricator-side-menu-view-css',
|
||||
'phabricator-crumbs-view-css',
|
||||
'phui-object-item-list-view-css',
|
||||
'global-drag-and-drop-css',
|
||||
'phui-spacing-css',
|
||||
'phui-form-css',
|
||||
'phui-icon-view-css',
|
||||
|
||||
'phabricator-application-launch-view-css',
|
||||
'phabricator-action-list-view-css',
|
||||
'phui-property-list-view-css',
|
||||
'phabricator-tag-view-css',
|
||||
'phui-list-view-css',
|
||||
),
|
||||
'differential.pkg.css' => array(
|
||||
'differential-core-view-css',
|
||||
'differential-changeset-view-css',
|
||||
'differential-results-table-css',
|
||||
'differential-revision-history-css',
|
||||
'differential-revision-list-css',
|
||||
'differential-table-of-contents-css',
|
||||
'differential-revision-comment-css',
|
||||
'differential-revision-add-comment-css',
|
||||
'differential-revision-comment-list-css',
|
||||
'phabricator-object-selector-css',
|
||||
'phabricator-content-source-view-css',
|
||||
'differential-local-commits-view-css',
|
||||
'inline-comment-summary-css',
|
||||
),
|
||||
'differential.pkg.js' => array(
|
||||
'phabricator-drag-and-drop-file-upload',
|
||||
'phabricator-shaped-request',
|
||||
|
||||
'javelin-behavior-differential-feedback-preview',
|
||||
'javelin-behavior-differential-edit-inline-comments',
|
||||
'javelin-behavior-differential-populate',
|
||||
'javelin-behavior-differential-show-more',
|
||||
'javelin-behavior-differential-diff-radios',
|
||||
'javelin-behavior-differential-accept-with-errors',
|
||||
'javelin-behavior-differential-comment-jump',
|
||||
'javelin-behavior-differential-add-reviewers-and-ccs',
|
||||
'javelin-behavior-differential-keyboard-navigation',
|
||||
'javelin-behavior-aphront-drag-and-drop-textarea',
|
||||
'javelin-behavior-phabricator-object-selector',
|
||||
'javelin-behavior-repository-crossreference',
|
||||
'javelin-behavior-load-blame',
|
||||
|
||||
'differential-inline-comment-editor',
|
||||
'javelin-behavior-differential-dropdown-menus',
|
||||
'javelin-behavior-differential-toggle-files',
|
||||
'javelin-behavior-differential-user-select',
|
||||
),
|
||||
'diffusion.pkg.css' => array(
|
||||
'diffusion-commit-view-css',
|
||||
'diffusion-icons-css',
|
||||
),
|
||||
'diffusion.pkg.js' => array(
|
||||
'javelin-behavior-diffusion-pull-lastmodified',
|
||||
'javelin-behavior-diffusion-commit-graph',
|
||||
'javelin-behavior-audit-preview',
|
||||
),
|
||||
'maniphest.pkg.css' => array(
|
||||
'maniphest-task-summary-css',
|
||||
'phabricator-project-tag-css',
|
||||
),
|
||||
'maniphest.pkg.js' => array(
|
||||
'javelin-behavior-maniphest-batch-selector',
|
||||
'javelin-behavior-maniphest-transaction-controls',
|
||||
'javelin-behavior-maniphest-transaction-preview',
|
||||
'javelin-behavior-maniphest-transaction-expand',
|
||||
'javelin-behavior-maniphest-subpriority-editor',
|
||||
),
|
||||
'darkconsole.pkg.js' => array(
|
||||
'javelin-behavior-dark-console',
|
||||
'javelin-behavior-error-log',
|
||||
),
|
||||
);
|
|
@ -1,9 +1,9 @@
|
|||
#!/bin/sh
|
||||
|
||||
echo "src/__celerity_resource_map__.php merge=celerity" \
|
||||
echo "resources/celerity/map.php merge=celerity" \
|
||||
>> `dirname "$0"`/../../.git/info/attributes
|
||||
|
||||
git config merge.celerity.name "Celerity Mapper"
|
||||
|
||||
git config merge.celerity.driver \
|
||||
'php $GIT_DIR/../scripts/celerity_mapper.php $GIT_DIR/../webroot'
|
||||
'php $GIT_DIR/../bin/celerity map'
|
||||
|
|
|
@ -1,397 +1,5 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
$package_spec = array(
|
||||
'javelin.pkg.js' => array(
|
||||
'javelin-util',
|
||||
'javelin-install',
|
||||
'javelin-event',
|
||||
'javelin-stratcom',
|
||||
'javelin-behavior',
|
||||
'javelin-resource',
|
||||
'javelin-request',
|
||||
'javelin-vector',
|
||||
'javelin-dom',
|
||||
'javelin-json',
|
||||
'javelin-uri',
|
||||
'javelin-workflow',
|
||||
'javelin-mask',
|
||||
'javelin-typeahead',
|
||||
'javelin-typeahead-normalizer',
|
||||
'javelin-typeahead-source',
|
||||
'javelin-typeahead-preloaded-source',
|
||||
'javelin-typeahead-ondemand-source',
|
||||
'javelin-tokenizer',
|
||||
'javelin-history',
|
||||
),
|
||||
'core.pkg.js' => array(
|
||||
'javelin-behavior-aphront-basic-tokenizer',
|
||||
'javelin-behavior-workflow',
|
||||
'javelin-behavior-aphront-form-disable-on-submit',
|
||||
'phabricator-keyboard-shortcut-manager',
|
||||
'phabricator-keyboard-shortcut',
|
||||
'javelin-behavior-phabricator-keyboard-shortcuts',
|
||||
'javelin-behavior-refresh-csrf',
|
||||
'javelin-behavior-phabricator-watch-anchor',
|
||||
'javelin-behavior-phabricator-autofocus',
|
||||
'phabricator-menu-item',
|
||||
'phabricator-dropdown-menu',
|
||||
'phabricator-phtize',
|
||||
'javelin-behavior-phabricator-oncopy',
|
||||
'phabricator-tooltip',
|
||||
'javelin-behavior-phabricator-tooltips',
|
||||
'phabricator-prefab',
|
||||
'javelin-behavior-device',
|
||||
'javelin-behavior-toggle-class',
|
||||
'javelin-behavior-lightbox-attachments',
|
||||
'phabricator-busy',
|
||||
'javelin-aphlict',
|
||||
'phabricator-notification',
|
||||
'javelin-behavior-aphlict-listen',
|
||||
'javelin-behavior-phabricator-search-typeahead',
|
||||
'javelin-behavior-konami',
|
||||
'javelin-behavior-aphlict-dropdown',
|
||||
'javelin-behavior-history-install',
|
||||
'javelin-behavior-phabricator-gesture',
|
||||
|
||||
'javelin-behavior-phabricator-active-nav',
|
||||
'javelin-behavior-phabricator-nav',
|
||||
'javelin-behavior-phabricator-remarkup-assist',
|
||||
'phabricator-textareautils',
|
||||
'phabricator-file-upload',
|
||||
'javelin-behavior-global-drag-and-drop',
|
||||
'javelin-behavior-phabricator-reveal-content',
|
||||
'phabricator-hovercard',
|
||||
'javelin-behavior-phabricator-hovercards',
|
||||
'javelin-color',
|
||||
'javelin-fx',
|
||||
),
|
||||
'core.pkg.css' => array(
|
||||
'phabricator-core-css',
|
||||
'phabricator-zindex-css',
|
||||
'phui-button-css',
|
||||
'phabricator-standard-page-view',
|
||||
'aphront-dialog-view-css',
|
||||
'phui-form-view-css',
|
||||
'aphront-panel-view-css',
|
||||
'aphront-table-view-css',
|
||||
'aphront-tokenizer-control-css',
|
||||
'aphront-typeahead-control-css',
|
||||
'aphront-list-filter-view-css',
|
||||
|
||||
'phabricator-jump-nav',
|
||||
|
||||
'phabricator-remarkup-css',
|
||||
'syntax-highlighting-css',
|
||||
'aphront-pager-view-css',
|
||||
'phabricator-transaction-view-css',
|
||||
'aphront-tooltip-css',
|
||||
'phabricator-flag-css',
|
||||
'aphront-error-view-css',
|
||||
|
||||
'sprite-icons-css',
|
||||
'sprite-gradient-css',
|
||||
'sprite-menu-css',
|
||||
'sprite-apps-large-css',
|
||||
'sprite-status-css',
|
||||
|
||||
'phabricator-main-menu-view',
|
||||
'phabricator-notification-css',
|
||||
'phabricator-notification-menu-css',
|
||||
'lightbox-attachment-css',
|
||||
'phui-header-view-css',
|
||||
'phabricator-filetree-view-css',
|
||||
'phabricator-nav-view-css',
|
||||
'phabricator-side-menu-view-css',
|
||||
'phabricator-crumbs-view-css',
|
||||
'phui-object-item-list-view-css',
|
||||
'global-drag-and-drop-css',
|
||||
'phui-spacing-css',
|
||||
'phui-form-css',
|
||||
'phui-icon-view-css',
|
||||
|
||||
'phabricator-application-launch-view-css',
|
||||
'phabricator-action-list-view-css',
|
||||
'phui-property-list-view-css',
|
||||
'phabricator-tag-view-css',
|
||||
'phui-list-view-css',
|
||||
),
|
||||
'differential.pkg.css' => array(
|
||||
'differential-core-view-css',
|
||||
'differential-changeset-view-css',
|
||||
'differential-results-table-css',
|
||||
'differential-revision-history-css',
|
||||
'differential-revision-list-css',
|
||||
'differential-table-of-contents-css',
|
||||
'differential-revision-comment-css',
|
||||
'differential-revision-add-comment-css',
|
||||
'differential-revision-comment-list-css',
|
||||
'phabricator-object-selector-css',
|
||||
'phabricator-content-source-view-css',
|
||||
'differential-local-commits-view-css',
|
||||
'inline-comment-summary-css',
|
||||
),
|
||||
'differential.pkg.js' => array(
|
||||
'phabricator-drag-and-drop-file-upload',
|
||||
'phabricator-shaped-request',
|
||||
|
||||
'javelin-behavior-differential-feedback-preview',
|
||||
'javelin-behavior-differential-edit-inline-comments',
|
||||
'javelin-behavior-differential-populate',
|
||||
'javelin-behavior-differential-show-more',
|
||||
'javelin-behavior-differential-diff-radios',
|
||||
'javelin-behavior-differential-accept-with-errors',
|
||||
'javelin-behavior-differential-comment-jump',
|
||||
'javelin-behavior-differential-add-reviewers-and-ccs',
|
||||
'javelin-behavior-differential-keyboard-navigation',
|
||||
'javelin-behavior-aphront-drag-and-drop-textarea',
|
||||
'javelin-behavior-phabricator-object-selector',
|
||||
'javelin-behavior-repository-crossreference',
|
||||
'javelin-behavior-load-blame',
|
||||
|
||||
'differential-inline-comment-editor',
|
||||
'javelin-behavior-differential-dropdown-menus',
|
||||
'javelin-behavior-differential-toggle-files',
|
||||
'javelin-behavior-differential-user-select',
|
||||
),
|
||||
'diffusion.pkg.css' => array(
|
||||
'diffusion-commit-view-css',
|
||||
'diffusion-icons-css',
|
||||
),
|
||||
'diffusion.pkg.js' => array(
|
||||
'javelin-behavior-diffusion-pull-lastmodified',
|
||||
'javelin-behavior-diffusion-commit-graph',
|
||||
'javelin-behavior-audit-preview',
|
||||
),
|
||||
'maniphest.pkg.css' => array(
|
||||
'maniphest-task-summary-css',
|
||||
'phabricator-project-tag-css',
|
||||
),
|
||||
'maniphest.pkg.js' => array(
|
||||
'javelin-behavior-maniphest-batch-selector',
|
||||
'javelin-behavior-maniphest-transaction-controls',
|
||||
'javelin-behavior-maniphest-transaction-preview',
|
||||
'javelin-behavior-maniphest-transaction-expand',
|
||||
'javelin-behavior-maniphest-subpriority-editor',
|
||||
),
|
||||
'darkconsole.pkg.js' => array(
|
||||
'javelin-behavior-dark-console',
|
||||
'javelin-behavior-error-log',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
require_once dirname(__FILE__).'/__init_script__.php';
|
||||
|
||||
$args = new PhutilArgumentParser($argv);
|
||||
$args->setTagline('map static resources');
|
||||
$args->setSynopsis(
|
||||
"**celerity_mapper.php** [--output __path__] [--with-custom] <webroot>");
|
||||
$args->parse(
|
||||
array(
|
||||
array(
|
||||
'name' => 'webroot',
|
||||
'wildcard' => true,
|
||||
),
|
||||
));
|
||||
|
||||
$root = $args->getArg('webroot');
|
||||
if (count($root) != 1 || !is_dir(reset($root))) {
|
||||
$args->printHelpAndExit();
|
||||
}
|
||||
$root = Filesystem::resolvePath(reset($root));
|
||||
|
||||
$celerity_path = Filesystem::resolvePath(
|
||||
'../src/__celerity_resource_map__.php',
|
||||
$root);
|
||||
|
||||
$resource_hash = PhabricatorEnv::getEnvConfig('celerity.resource-hash');
|
||||
$runtime_map = array();
|
||||
|
||||
echo "Finding raw static resources...\n";
|
||||
$finder = id(new FileFinder($root))
|
||||
->withType('f')
|
||||
->withSuffix('png')
|
||||
->withSuffix('jpg')
|
||||
->withSuffix('gif')
|
||||
->withSuffix('swf')
|
||||
->withFollowSymlinks(true)
|
||||
->setGenerateChecksums(true);
|
||||
$raw_files = $finder->find();
|
||||
|
||||
echo "Processing ".count($raw_files)." files";
|
||||
foreach ($raw_files as $path => $hash) {
|
||||
echo ".";
|
||||
$path = '/'.Filesystem::readablePath($path, $root);
|
||||
$type = CelerityResourceTransformer::getResourceType($path);
|
||||
|
||||
$hash = md5($hash.$path.$resource_hash);
|
||||
$uri = '/res/'.substr($hash, 0, 8).$path;
|
||||
|
||||
$runtime_map[$path] = array(
|
||||
'hash' => $hash,
|
||||
'uri' => $uri,
|
||||
'disk' => $path,
|
||||
'type' => $type,
|
||||
);
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
$xformer = id(new CelerityResourceTransformer())
|
||||
->setMinify(false)
|
||||
->setRawResourceMap($runtime_map);
|
||||
|
||||
echo "Finding transformable static resources...\n";
|
||||
$finder = id(new FileFinder($root))
|
||||
->withType('f')
|
||||
->withSuffix('js')
|
||||
->withSuffix('css')
|
||||
->withFollowSymlinks(true)
|
||||
->setGenerateChecksums(true);
|
||||
$files = $finder->find();
|
||||
|
||||
echo "Processing ".count($files)." files";
|
||||
|
||||
$file_map = array();
|
||||
foreach ($files as $path => $raw_hash) {
|
||||
echo ".";
|
||||
$path = '/'.Filesystem::readablePath($path, $root);
|
||||
$data = Filesystem::readFile($root.$path);
|
||||
|
||||
$data = $xformer->transformResource($path, $data);
|
||||
$hash = md5($data);
|
||||
$hash = md5($hash.$path.$resource_hash);
|
||||
|
||||
$file_map[$path] = array(
|
||||
'hash' => $hash,
|
||||
'disk' => $path,
|
||||
);
|
||||
}
|
||||
echo "\n";
|
||||
|
||||
$resource_graph = array();
|
||||
$hash_map = array();
|
||||
|
||||
$parser = new PhutilDocblockParser();
|
||||
foreach ($file_map as $path => $info) {
|
||||
$type = CelerityResourceTransformer::getResourceType($path);
|
||||
|
||||
$data = Filesystem::readFile($root.$info['disk']);
|
||||
$matches = array();
|
||||
$ok = preg_match('@/[*][*].*?[*]/@s', $data, $matches);
|
||||
if (!$ok) {
|
||||
throw new Exception(
|
||||
"File {$path} does not have a header doc comment. Encode dependency ".
|
||||
"data in a header docblock.");
|
||||
}
|
||||
|
||||
list($description, $metadata) = $parser->parse($matches[0]);
|
||||
|
||||
$provides = preg_split('/\s+/', trim(idx($metadata, 'provides')));
|
||||
$requires = preg_split('/\s+/', trim(idx($metadata, 'requires')));
|
||||
$provides = array_filter($provides);
|
||||
$requires = array_filter($requires);
|
||||
|
||||
if (!$provides) {
|
||||
// Tests and documentation-only JS is permitted to @provide no targets.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count($provides) > 1) {
|
||||
throw new Exception(
|
||||
"File {$path} must @provide at most one Celerity target.");
|
||||
}
|
||||
|
||||
$provides = reset($provides);
|
||||
|
||||
$uri = '/res/'.substr($info['hash'], 0, 8).$path;
|
||||
|
||||
$hash_map[$provides] = $info['hash'];
|
||||
|
||||
$resource_graph[$provides] = $requires;
|
||||
|
||||
$runtime_map[$provides] = array(
|
||||
'uri' => $uri,
|
||||
'type' => $type,
|
||||
'requires' => $requires,
|
||||
'disk' => $path,
|
||||
);
|
||||
}
|
||||
|
||||
$celerity_resource_graph = new CelerityResourceGraph();
|
||||
$celerity_resource_graph->addNodes($resource_graph);
|
||||
$celerity_resource_graph->setResourceGraph($resource_graph);
|
||||
$celerity_resource_graph->loadGraph();
|
||||
|
||||
foreach ($resource_graph as $provides => $requires) {
|
||||
$cycle = $celerity_resource_graph->detectCycles($provides);
|
||||
if ($cycle) {
|
||||
throw new Exception(
|
||||
"Cycle detected in resource graph: ". implode($cycle, " => ")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$package_map = array();
|
||||
foreach ($package_spec as $name => $package) {
|
||||
$hashes = array();
|
||||
$type = null;
|
||||
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.");
|
||||
}
|
||||
if ($type === null) {
|
||||
$type = $runtime_map[$symbol]['type'];
|
||||
} else {
|
||||
$ntype = $runtime_map[$symbol]['type'];
|
||||
if ($type !== $ntype) {
|
||||
throw new Exception(
|
||||
"Package specification for '{$name}' mixes resources of type ".
|
||||
"'{$type}' with resources of type '{$ntype}'. Each package may only ".
|
||||
"contain one type of resource.");
|
||||
}
|
||||
}
|
||||
$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' => $type,
|
||||
);
|
||||
foreach ($package as $symbol) {
|
||||
$package_map['reverse'][$symbol] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
ksort($runtime_map);
|
||||
$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['packages'] = isort($package_map['packages'], 'name');
|
||||
ksort($package_map['reverse']);
|
||||
$package_map = var_export($package_map, true);
|
||||
$package_map = preg_replace('/\s+$/m', '', $package_map);
|
||||
$package_map = preg_replace('/array \(/', 'array(', $package_map);
|
||||
|
||||
$generated = '@'.'generated';
|
||||
$resource_map = <<<EOFILE
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is automatically generated. Use 'celerity_mapper.php' to rebuild
|
||||
* it.
|
||||
* {$generated}
|
||||
*/
|
||||
|
||||
celerity_register_resource_map({$runtime_map}, {$package_map});
|
||||
|
||||
EOFILE;
|
||||
|
||||
echo "Writing map...\n";
|
||||
Filesystem::writeFile($celerity_path, $resource_map);
|
||||
echo "Done.\n";
|
||||
echo "This script is obsolete. Run `bin/celerity map` instead.\n";
|
||||
exit(1);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2408,7 +2408,6 @@ phutil_register_library_map(array(
|
|||
'_phabricator_time_format' => 'view/viewutils.php',
|
||||
'celerity_generate_unique_node_id' => 'infrastructure/celerity/api.php',
|
||||
'celerity_get_resource_uri' => 'infrastructure/celerity/api.php',
|
||||
'celerity_register_resource_map' => 'infrastructure/celerity/map.php',
|
||||
'implode_selected_handle_links' => 'applications/phid/handle/view/render.php',
|
||||
'javelin_tag' => 'infrastructure/javelin/markup.php',
|
||||
'phabricator_date' => 'view/viewutils.php',
|
||||
|
|
|
@ -73,7 +73,6 @@ class AphrontDefaultApplicationConfiguration
|
|||
return array(
|
||||
'/res/' => array(
|
||||
'(?:(?P<mtime>[0-9]+)T/)?'.
|
||||
'(?P<package>pkg/)?'.
|
||||
'(?P<hash>[a-f0-9]{8})/'.
|
||||
'(?P<path>.+\.(?:css|js|jpg|png|swf|gif))'
|
||||
=> 'CelerityPhabricatorResourceController',
|
||||
|
|
|
@ -39,7 +39,7 @@ final class PhameResourceController extends CelerityResourceController {
|
|||
$spec = $skin->getSpecification();
|
||||
|
||||
$this->root = $spec->getRootDirectory().DIRECTORY_SEPARATOR;
|
||||
return $this->serveResource($this->name, $package_hash = null);
|
||||
return $this->serveResource($this->name);
|
||||
}
|
||||
|
||||
protected function buildResourceTransformer() {
|
||||
|
|
|
@ -55,10 +55,8 @@ neessary preprocessing. It uses @{class:CelerityResourceMap} to locate resources
|
|||
and read packaging rules.
|
||||
|
||||
The dependency and packaging maps are generated by
|
||||
##scripts/celerity_mapper.php##, which updates
|
||||
##src/__celerity_resource_map__.php##. This file is automatically included and
|
||||
just calls @{function:celerity_register_resource_map} with a large blob of
|
||||
static data to populate @{class:CelerityResourceMap}.
|
||||
##bin/celerity map##, which updates
|
||||
##resources/celerity/map.php##..
|
||||
|
||||
@{class:CelerityStaticResourceResponse} also manages some Javelin information,
|
||||
and @{function:celerity_generate_unique_node_id} uses this metadata to provide
|
||||
|
|
|
@ -58,7 +58,7 @@ If you've only changed file content things will generally work even if you
|
|||
don't, but they might start not working as well in the future if you skip this
|
||||
step.
|
||||
|
||||
The generated file `src/__celerity_resource_map__.php` causes merge conflicts
|
||||
The generated file `resources/celerity/map.php` causes merge conflicts
|
||||
quite often. They can be resolved by running the Celerity mapper. You can
|
||||
automate this process by running:
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ final class CelerityPhabricatorResourceController
|
|||
|
||||
private $path;
|
||||
private $hash;
|
||||
private $package;
|
||||
|
||||
protected function getRootDirectory() {
|
||||
$root = dirname(phutil_get_library_root('phabricator'));
|
||||
|
@ -22,15 +21,10 @@ final class CelerityPhabricatorResourceController
|
|||
public function willProcessRequest(array $data) {
|
||||
$this->path = $data['path'];
|
||||
$this->hash = $data['hash'];
|
||||
$this->package = !empty($data['package']);
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$package_hash = null;
|
||||
if ($this->package) {
|
||||
$package_hash = $this->hash;
|
||||
}
|
||||
return $this->serveResource($this->path, $package_hash);
|
||||
return $this->serveResource($this->path);
|
||||
}
|
||||
|
||||
protected function buildResourceTransformer() {
|
||||
|
|
|
@ -39,8 +39,8 @@ abstract class CelerityResourceController extends PhabricatorController {
|
|||
|
||||
$map = CelerityResourceMap::getInstance();
|
||||
|
||||
if ($package_hash) {
|
||||
$resource_names = $map->getResourceNamesForPackageHash($package_hash);
|
||||
if ($map->isPackageResource($path)) {
|
||||
$resource_names = $map->getResourceNamesForPackageName($path);
|
||||
if (!$resource_names) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
|
|
@ -5,36 +5,46 @@
|
|||
* resources, resource dependencies, and packaging information. You generally do
|
||||
* not need to invoke it directly; instead, you call higher-level Celerity APIs
|
||||
* and it uses the resource map to satisfy your requests.
|
||||
*
|
||||
* @group celerity
|
||||
*/
|
||||
final class CelerityResourceMap {
|
||||
|
||||
private static $instance;
|
||||
private $resourceMap;
|
||||
|
||||
private $resources;
|
||||
private $symbolMap;
|
||||
private $requiresMap;
|
||||
private $packageMap;
|
||||
private $reverseMap;
|
||||
private $nameMap;
|
||||
private $hashMap;
|
||||
|
||||
public function __construct(CelerityResources $resources) {
|
||||
$this->resources = $resources;
|
||||
|
||||
$map = $resources->loadMap();
|
||||
$this->symbolMap = idx($map, 'symbols', array());
|
||||
$this->requiresMap = idx($map, 'requires', array());
|
||||
$this->packageMap = idx($map, 'packages', array());
|
||||
$this->nameMap = idx($map, 'names', array());
|
||||
|
||||
// We derive these reverse maps at runtime.
|
||||
|
||||
$this->hashMap = array_flip($this->nameMap);
|
||||
$this->componentMap = array();
|
||||
foreach ($this->packageMap as $package_name => $symbols) {
|
||||
foreach ($symbols as $symbol) {
|
||||
$this->componentMap[$symbol] = $package_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function getInstance() {
|
||||
if (empty(self::$instance)) {
|
||||
self::$instance = new CelerityResourceMap();
|
||||
$root = phutil_get_library_root('phabricator');
|
||||
|
||||
$path = '__celerity_resource_map__.php';
|
||||
$ok = include_once $root.'/'.$path;
|
||||
if (!$ok) {
|
||||
throw new Exception(
|
||||
"Failed to load Celerity resource map!");
|
||||
}
|
||||
$resources = new CelerityPhabricatorResources();
|
||||
self::$instance = new CelerityResourceMap($resources);
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function setResourceMap($resource_map) {
|
||||
$this->resourceMap = $resource_map;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPackagedNamesForSymbols(array $symbols) {
|
||||
$resolved = $this->resolveResources($symbols);
|
||||
return $this->packageResources($resolved);
|
||||
|
@ -53,91 +63,71 @@ final class CelerityResourceMap {
|
|||
}
|
||||
|
||||
private function resolveResource(array &$map, $symbol) {
|
||||
if (empty($this->resourceMap[$symbol])) {
|
||||
if (empty($this->symbolMap[$symbol])) {
|
||||
throw new Exception(
|
||||
"Attempting to resolve unknown Celerity resource, '{$symbol}'.");
|
||||
pht(
|
||||
'Attempting to resolve unknown resource, "%s".',
|
||||
$symbol));
|
||||
}
|
||||
|
||||
$info = $this->resourceMap[$symbol];
|
||||
foreach ($info['requires'] as $requires) {
|
||||
if (!empty($map[$requires])) {
|
||||
$hash = $this->symbolMap[$symbol];
|
||||
|
||||
$map[$symbol] = $hash;
|
||||
|
||||
if (isset($this->requiresMap[$hash])) {
|
||||
$requires = $this->requiresMap[$hash];
|
||||
} else {
|
||||
$requires = array();
|
||||
}
|
||||
|
||||
foreach ($requires as $required_symbol) {
|
||||
if (!empty($map[$required_symbol])) {
|
||||
continue;
|
||||
}
|
||||
$this->resolveResource($map, $requires);
|
||||
$this->resolveResource($map, $required_symbol);
|
||||
}
|
||||
|
||||
$map[$symbol] = $info;
|
||||
}
|
||||
|
||||
public function setPackageMap($package_map) {
|
||||
$this->packageMap = $package_map;
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function packageResources(array $resolved_map) {
|
||||
$packaged = array();
|
||||
$handled = array();
|
||||
foreach ($resolved_map as $symbol => $info) {
|
||||
foreach ($resolved_map as $symbol => $hash) {
|
||||
if (isset($handled[$symbol])) {
|
||||
continue;
|
||||
}
|
||||
if (empty($this->packageMap['reverse'][$symbol])) {
|
||||
$packaged[$symbol] = $info;
|
||||
|
||||
if (empty($this->componentMap[$symbol])) {
|
||||
$packaged[] = $this->hashMap[$hash];
|
||||
} else {
|
||||
$package = $this->packageMap['reverse'][$symbol];
|
||||
$package_info = $this->packageMap['packages'][$package];
|
||||
$packaged[$package_info['name']] = $package_info;
|
||||
foreach ($package_info['symbols'] as $packaged_symbol) {
|
||||
$handled[$packaged_symbol] = true;
|
||||
$package_name = $this->componentMap[$symbol];
|
||||
$packaged[] = $package_name;
|
||||
|
||||
$package_symbols = $this->packageMap[$package_name];
|
||||
foreach ($package_symbols as $package_symbol) {
|
||||
$handled[$package_symbol] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$names = array();
|
||||
foreach ($packaged as $key => $resource) {
|
||||
if (isset($resource['disk'])) {
|
||||
$names[] = $resource['disk'];
|
||||
} else {
|
||||
$names[] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
return $names;
|
||||
return $packaged;
|
||||
}
|
||||
|
||||
public function getResourceDataForName($resource_name) {
|
||||
$root = phutil_get_library_root('phabricator');
|
||||
$root = dirname($root).'/webroot/';
|
||||
return Filesystem::readFile($root.$resource_name);
|
||||
return $this->resources->getResourceData($resource_name);
|
||||
}
|
||||
|
||||
public function getResourceNamesForPackageHash($package_hash) {
|
||||
$package = idx($this->packageMap['packages'], $package_hash);
|
||||
if (!$package) {
|
||||
public function getResourceNamesForPackageName($package_name) {
|
||||
$package_symbols = idx($this->packageMap, $package_name);
|
||||
if (!$package_symbols) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$paths = array();
|
||||
foreach ($package['symbols'] as $symbol) {
|
||||
$paths[] = $this->resourceMap[$symbol]['disk'];
|
||||
$resource_names = array();
|
||||
foreach ($package_symbols as $symbol) {
|
||||
$resource_names[] = $this->hashMap[$this->symbolMap[$symbol]];
|
||||
}
|
||||
|
||||
return $paths;
|
||||
}
|
||||
|
||||
private function lookupSymbolInformation($symbol) {
|
||||
return idx($this->resourceMap, $symbol);
|
||||
}
|
||||
|
||||
private function lookupFileInformation($path) {
|
||||
if (empty($this->reverseMap)) {
|
||||
$this->reverseMap = array();
|
||||
foreach ($this->resourceMap as $symbol => $data) {
|
||||
$data['provides'] = $symbol;
|
||||
$this->reverseMap[$data['disk']] = $data;
|
||||
}
|
||||
}
|
||||
return idx($this->reverseMap, $path);
|
||||
return $resource_names;
|
||||
}
|
||||
|
||||
|
||||
|
@ -148,32 +138,18 @@ final class CelerityResourceMap {
|
|||
* @return int Epoch timestamp of last resource modification.
|
||||
*/
|
||||
public function getModifiedTimeForName($name) {
|
||||
$package_hash = null;
|
||||
foreach ($this->packageMap['packages'] as $hash => $package) {
|
||||
if ($package['name'] == $name) {
|
||||
$package_hash = $hash;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$root = dirname(phutil_get_library_root('phabricator')).'/webroot';
|
||||
|
||||
$mtime = 0;
|
||||
|
||||
if ($package_hash) {
|
||||
$names = $this->getResourceNamesForPackageHash($package_hash);
|
||||
foreach ($names as $component_name) {
|
||||
$info = $this->lookupFileInformation($component_name);
|
||||
if ($info) {
|
||||
$mtime = max($mtime, (int)filemtime($root.$info['disk']));
|
||||
}
|
||||
if ($this->isPackageResource($name)) {
|
||||
$names = array();
|
||||
foreach ($this->packageMap[$name] as $symbol) {
|
||||
$names[] = $this->getResourceNameForSymbol($symbol);
|
||||
}
|
||||
} else {
|
||||
$info = $this->lookupFileInformation($name);
|
||||
if ($info) {
|
||||
$root = dirname(phutil_get_library_root('phabricator')).'/webroot';
|
||||
$mtime = (int)filemtime($root.$info['disk']);
|
||||
}
|
||||
$names = array($name);
|
||||
}
|
||||
|
||||
$mtime = 0;
|
||||
foreach ($names as $name) {
|
||||
$mtime = max($mtime, $this->resources->getResourceModifiedTime($name));
|
||||
}
|
||||
|
||||
return $mtime;
|
||||
|
@ -185,15 +161,11 @@ final class CelerityResourceMap {
|
|||
* method is fairly low-level and ignores packaging.
|
||||
*
|
||||
* @param string Resource symbol to lookup.
|
||||
* @return string|null Fully-qualified resource URI, or null if the symbol
|
||||
* is unknown.
|
||||
* @return string|null Resource URI, or null if the symbol is unknown.
|
||||
*/
|
||||
public function getURIForSymbol($symbol) {
|
||||
$info = $this->lookupSymbolInformation($symbol);
|
||||
if ($info) {
|
||||
return idx($info, 'uri');
|
||||
}
|
||||
return null;
|
||||
$hash = idx($this->symbolMap, $symbol);
|
||||
return $this->getURIForHash($hash);
|
||||
}
|
||||
|
||||
|
||||
|
@ -202,22 +174,26 @@ final class CelerityResourceMap {
|
|||
* This method is fairly low-level and ignores packaging.
|
||||
*
|
||||
* @param string Resource name to lookup.
|
||||
* @return string|null Fully-qualified resource URI, or null if the name
|
||||
* is unknown.
|
||||
* @return string|null Resource URI, or null if the name is unknown.
|
||||
*/
|
||||
public function getURIForName($name) {
|
||||
$info = $this->lookupFileInformation($name);
|
||||
if ($info) {
|
||||
return idx($info, 'uri');
|
||||
}
|
||||
$hash = idx($this->nameMap, $name);
|
||||
return $this->getURIForHash($hash);
|
||||
}
|
||||
|
||||
foreach ($this->packageMap['packages'] as $hash => $package) {
|
||||
if ($package['name'] == $name) {
|
||||
return $package['uri'];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
/**
|
||||
* Return the absolute URI for a resource, identified by hash.
|
||||
* This method is fairly low-level and ignores packaging.
|
||||
*
|
||||
* @param string Resource hash to lookup.
|
||||
* @return string|null Resource URI, or null if the hash is unknown.
|
||||
*/
|
||||
private function getURIForHash($hash) {
|
||||
if ($hash === null) {
|
||||
return null;
|
||||
}
|
||||
return $this->resources->getResourceURI($hash, $this->hashMap[$hash]);
|
||||
}
|
||||
|
||||
|
||||
|
@ -229,11 +205,11 @@ final class CelerityResourceMap {
|
|||
* is unknown.
|
||||
*/
|
||||
public function getRequiredSymbolsForName($name) {
|
||||
$info = $this->lookupFileInformation($name);
|
||||
if ($info) {
|
||||
return idx($info, 'requires', array());
|
||||
$hash = idx($this->symbolMap, $name);
|
||||
if ($hash === null) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
return idx($this->requiresMap, $hash, array());
|
||||
}
|
||||
|
||||
|
||||
|
@ -244,12 +220,12 @@ final class CelerityResourceMap {
|
|||
* @return string|null Resource name, or null if the symbol is unknown.
|
||||
*/
|
||||
public function getResourceNameForSymbol($symbol) {
|
||||
$info = $this->lookupSymbolInformation($symbol);
|
||||
if ($info) {
|
||||
return idx($info, 'disk');
|
||||
}
|
||||
return null;
|
||||
$hash = idx($this->symbolMap, $symbol);
|
||||
return idx($this->hashMap, $hash);
|
||||
}
|
||||
|
||||
public function isPackageResource($name) {
|
||||
return isset($this->packageMap[$name]);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,10 +15,17 @@ final class CelerityManagementMapWorkflow
|
|||
public function execute(PhutilArgumentParser $args) {
|
||||
$resources_map = CelerityResources::getAll();
|
||||
|
||||
$this->log(
|
||||
pht(
|
||||
"Rebuilding %d resource source(s).",
|
||||
new PhutilNumber(count($resources_map))));
|
||||
|
||||
foreach ($resources_map as $name => $resources) {
|
||||
$this->rebuildResources($resources);
|
||||
}
|
||||
|
||||
$this->log(pht("Done."));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -29,20 +36,36 @@ final class CelerityManagementMapWorkflow
|
|||
* @return void
|
||||
*/
|
||||
private function rebuildResources(CelerityResources $resources) {
|
||||
$this->log(
|
||||
pht(
|
||||
'Rebuilding resource source "%s" (%s)...',
|
||||
$resources->getName(),
|
||||
get_class($resources)));
|
||||
|
||||
$binary_map = $this->rebuildBinaryResources($resources);
|
||||
|
||||
$this->log(
|
||||
pht(
|
||||
'Found %d binary resources.',
|
||||
new PhutilNumber(count($binary_map))));
|
||||
|
||||
$xformer = id(new CelerityResourceTransformer())
|
||||
->setMinify(false)
|
||||
->setRawURIMap(ipull($binary_map, 'uri'));
|
||||
|
||||
$text_map = $this->rebuildTextResources($resources, $xformer);
|
||||
|
||||
$this->log(
|
||||
pht(
|
||||
'Found %d text resources.',
|
||||
new PhutilNumber(count($text_map))));
|
||||
|
||||
$resource_graph = array();
|
||||
$requires_map = array();
|
||||
$provides_map = array();
|
||||
$symbol_map = array();
|
||||
foreach ($text_map as $name => $info) {
|
||||
if (isset($info['provides'])) {
|
||||
$provides_map[$info['provides']] = $info['hash'];
|
||||
$symbol_map[$info['provides']] = $info['hash'];
|
||||
|
||||
// We only need to check for cycles and add this to the requires map
|
||||
// if it actually requires anything.
|
||||
|
@ -54,15 +77,49 @@ final class CelerityManagementMapWorkflow
|
|||
}
|
||||
|
||||
$this->detectGraphCycles($resource_graph);
|
||||
$name_map = ipull($binary_map, 'hash') + ipull($text_map, 'hash');
|
||||
$hash_map = array_flip($name_map);
|
||||
|
||||
$hash_map = ipull($binary_map, 'hash') + ipull($text_map, 'hash');
|
||||
$package_map = $this->rebuildPackages(
|
||||
$resources,
|
||||
$symbol_map,
|
||||
$hash_map);
|
||||
|
||||
$this->log(
|
||||
pht(
|
||||
'Found %d packages.',
|
||||
new PhutilNumber(count($package_map))));
|
||||
|
||||
// TODO: Actually do things.
|
||||
$component_map = array();
|
||||
foreach ($package_map as $package_name => $package_info) {
|
||||
foreach ($package_info['symbols'] as $symbol) {
|
||||
$component_map[$symbol] = $package_name;
|
||||
}
|
||||
}
|
||||
|
||||
var_dump($provides_map);
|
||||
var_dump($requires_map);
|
||||
var_dump($hash_map);
|
||||
$name_map = $this->mergeNameMaps(
|
||||
array(
|
||||
array(pht('Binary'), ipull($binary_map, 'hash')),
|
||||
array(pht('Text'), ipull($text_map, 'hash')),
|
||||
array(pht('Package'), ipull($package_map, 'hash')),
|
||||
));
|
||||
$package_map = ipull($package_map, 'symbols');
|
||||
|
||||
ksort($name_map);
|
||||
ksort($symbol_map);
|
||||
ksort($requires_map);
|
||||
ksort($package_map);
|
||||
|
||||
$map_content = $this->formatMapContent(array(
|
||||
'names' => $name_map,
|
||||
'symbols' => $symbol_map,
|
||||
'requires' => $requires_map,
|
||||
'packages' => $package_map,
|
||||
));
|
||||
|
||||
$map_path = $resources->getPathToMap();
|
||||
$this->log(pht('Writing map "%s".', Filesystem::readablePath($map_path)));
|
||||
Filesystem::writeFile($map_path, $map_content);
|
||||
}
|
||||
|
||||
|
||||
|
@ -202,4 +259,112 @@ final class CelerityManagementMapWorkflow
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build package specifications for a given resource source.
|
||||
*
|
||||
* @param CelerityResources Resource source to rebuild.
|
||||
* @param list<string, string> Map of `@provides` to hashes.
|
||||
* @param list<string, string> Map of hashes to resource names.
|
||||
* @return map<string, map<string, string>> Package information maps.
|
||||
*/
|
||||
private function rebuildPackages(
|
||||
CelerityResources $resources,
|
||||
array $symbol_map,
|
||||
array $reverse_map) {
|
||||
|
||||
$package_map = array();
|
||||
|
||||
$package_spec = $resources->getResourcePackages();
|
||||
foreach ($package_spec as $package_name => $package_symbols) {
|
||||
$type = null;
|
||||
$hashes = array();
|
||||
foreach ($package_symbols as $symbol) {
|
||||
$symbol_hash = idx($symbol_map, $symbol);
|
||||
if ($symbol_hash === null) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Package specification for "%s" includes "%s", but that symbol '.
|
||||
'is not @provided by any resource.',
|
||||
$package_name,
|
||||
$symbol));
|
||||
}
|
||||
|
||||
$resource_name = $reverse_map[$symbol_hash];
|
||||
$resource_type = $resources->getResourceType($resource_name);
|
||||
if ($type === null) {
|
||||
$type = $resource_type;
|
||||
} else if ($type !== $resource_type) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Package specification for "%s" includes resources of multiple '.
|
||||
'types (%s, %s). Each package may only contain one type of '.
|
||||
'resource.',
|
||||
$package_name,
|
||||
$type,
|
||||
$resource_type));
|
||||
}
|
||||
|
||||
$hashes[] = $symbol.':'.$symbol_hash;
|
||||
}
|
||||
|
||||
$hash = $resources->getCelerityHash(implode("\n", $hashes));
|
||||
$package_map[$package_name] = array(
|
||||
'hash' => $hash,
|
||||
'symbols' => $package_symbols,
|
||||
);
|
||||
}
|
||||
|
||||
return $package_map;
|
||||
}
|
||||
|
||||
private function mergeNameMaps(array $maps) {
|
||||
$result = array();
|
||||
$origin = array();
|
||||
foreach ($maps as $map) {
|
||||
list($map_name, $data) = $map;
|
||||
foreach ($data as $name => $hash) {
|
||||
if (empty($result[$name])) {
|
||||
$result[$name] = $hash;
|
||||
$origin[$name] = $map_name;
|
||||
} else {
|
||||
$old = $origin[$name];
|
||||
$new = $map_name;
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Resource source defines two resources with the same name, '.
|
||||
'"%s". One is defined in the "%s" map; the other in the "%s" '.
|
||||
'map. Each resource must have a unique name.',
|
||||
$name,
|
||||
$old,
|
||||
$new));
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function log($message) {
|
||||
$console = PhutilConsole::getConsole();
|
||||
$console->writeErr("%s\n", $message);
|
||||
}
|
||||
|
||||
private function formatMapContent(array $data) {
|
||||
$content = var_export($data, true);
|
||||
$content = preg_replace('/\s+$/m', '', $content);
|
||||
$content = preg_replace('/array \(/', 'array(', $content);
|
||||
|
||||
$generated = '@'.'generated';
|
||||
return <<<EOFILE
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is automatically generated. Use 'bin/celerity map' to rebuild it.
|
||||
* {$generated}
|
||||
*/
|
||||
return {$content};
|
||||
|
||||
EOFILE;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Registers a resource map for Celerity. This is glue code between the Celerity
|
||||
* mapper script and @{class:CelerityResourceMap}.
|
||||
*
|
||||
* @group celerity
|
||||
*/
|
||||
function celerity_register_resource_map(array $map, array $package_map) {
|
||||
$instance = CelerityResourceMap::getInstance();
|
||||
$instance->setResourceMap($map);
|
||||
$instance->setPackageMap($package_map);
|
||||
}
|
|
@ -21,4 +21,8 @@ final class CelerityPhabricatorResources extends CelerityResourcesOnDisk {
|
|||
return dirname(phutil_get_library_root('phabricator')).'/'.$to_file;
|
||||
}
|
||||
|
||||
public function getResourcePackages() {
|
||||
return include $this->getPhabricatorPath('resources/celerity/packages.php');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,11 +5,14 @@
|
|||
*/
|
||||
abstract class CelerityResources {
|
||||
|
||||
private $map;
|
||||
|
||||
abstract public function getName();
|
||||
abstract public function getPathToMap();
|
||||
abstract public function getResourceData($name);
|
||||
abstract public function findBinaryResources();
|
||||
abstract public function findTextResources();
|
||||
abstract public function getResourceModifiedTime($name);
|
||||
|
||||
public function getCelerityHash($data) {
|
||||
$tail = PhabricatorEnv::getEnvConfig('celerity.resource-hash');
|
||||
|
@ -25,6 +28,17 @@ abstract class CelerityResources {
|
|||
return "/res/{$hash}/{$name}";
|
||||
}
|
||||
|
||||
public function getResourcePackages() {
|
||||
return array();
|
||||
}
|
||||
|
||||
public function loadMap() {
|
||||
if ($this->map === null) {
|
||||
$this->map = include $this->getPathToMap();
|
||||
}
|
||||
return $this->map;
|
||||
}
|
||||
|
||||
public static function getAll() {
|
||||
static $resources_map;
|
||||
if ($resources_map === null) {
|
||||
|
|
|
@ -7,8 +7,12 @@ abstract class CelerityResourcesOnDisk extends CelerityResources {
|
|||
|
||||
abstract public function getPathToResources();
|
||||
|
||||
private function getPathToResource($name) {
|
||||
return $this->getPathToResources().DIRECTORY_SEPARATOR.$name;
|
||||
}
|
||||
|
||||
public function getResourceData($name) {
|
||||
return Filesystem::readFile($this->getPathToResources().'/'.$name);
|
||||
return Filesystem::readFile($this->getPathToResource($name));
|
||||
}
|
||||
|
||||
public function findBinaryResources() {
|
||||
|
@ -19,6 +23,10 @@ abstract class CelerityResourcesOnDisk extends CelerityResources {
|
|||
return $this->findResourcesWithSuffixes($this->getTextFileSuffixes());
|
||||
}
|
||||
|
||||
public function getResourceModifiedTime($name) {
|
||||
return (int)filemtime($this->getPathToResource($name));
|
||||
}
|
||||
|
||||
protected function getBinaryFileSuffixes() {
|
||||
return array(
|
||||
'png',
|
||||
|
|
Loading…
Reference in a new issue