diff --git a/src/docs/flavortext/soon_static_resources.diviner b/src/docs/flavortext/soon_static_resources.diviner
index 7b8139bc73..1a663fea26 100644
--- a/src/docs/flavortext/soon_static_resources.diviner
+++ b/src/docs/flavortext/soon_static_resources.diviner
@@ -1,10 +1,13 @@
@title Things You Should Do Soon: Static Resources
@group flavortext
-
Over time, you'll write more JS and CSS and eventually need to put systems in
place to manage it.
+This is part of @{article:Things You Should Do Soon}, which describes
+architectural problems in web applications which you should begin to consider
+before you encounter them.
+
= Manage Dependencies Automatically =
The naive way to add static resources to a page is to include them at the top
diff --git a/src/docs/technical/celerity.diviner b/src/docs/technical/celerity.diviner
new file mode 100644
index 0000000000..9c707565ce
--- /dev/null
+++ b/src/docs/technical/celerity.diviner
@@ -0,0 +1,65 @@
+@title Celerity Technical Documentation
+@group celerity
+
+Technical overview of the Celerity system.
+
+= Overview =
+
+Celerity is a static resource (CSS and JS) management system, which handles:
+
+ - Keeping track of which resources a page needs.
+ - Generating URIs for the browser to access resources.
+ - Managing dependencies between resources.
+ - Packaging resources into fewer HTTP requests for performance.
+ - Preprocessing resources (e.g., stripping comments and whitespace).
+ - Delivering resources and managing resource cache lifetimes.
+ - Interfacing with the client to manage resources.
+
+Celerity is an outgrowth of the //Haste// system at Facebook. You can find more
+information about Celerity here:
+
+ - @{article:Things You Should Do Soon: Static Resources} describes the history
+ and context of the system and the problems it solves.
+ - @{article:Adding New CSS and JS} provides a developer guide to using
+ Celerity.
+
+= Class Relationships =
+
+Celerity's primary API is @{function:require_celerity_resource}, which marks a
+resource for inclusion when a response is rendered (e.g., when the HTML page is
+generated, or when the response to an Ajax request is built). For instance, if
+you use a CSS class like "widget-view", you must ensure the appropriate CSS is
+included by calling ##require_celerity_resource('widget-view-css')## (or
+similar), at your use site.
+
+This function uses @{class:CelerityAPI} to access the active
+@{class:CelerityStaticResourceResponse} and tells it that it needs to include
+the resource later, when the response actually gets built. (This layer of
+indirection provides future-proofing against certain complex situations Facebook
+eventually encountered).
+
+When the time comes to render the response, the page renderer uses
+@{class:CelerityAPI} to access the active
+@{class:CelerityStaticResourceResponse} and requests that it render out
+appropriate references to CSS and JS resources. It uses
+@{class:CelerityResourceMap} to determine the dependencies for the requested
+resources (so you only have to explicitly include what you're actually using,
+and not all of its dependencies) and any packaging rules (so it may be able to
+generate fewer resource requests, improving performance). It then generates
+#### and #### references to these resources.
+
+These references point at ##/res/## URIs, which are handled by
+@{class:CelerityResourceController}. It responds to these requests and delivers
+the relevant resources and packages, managing cache lifetimes and handling any
+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}.
+
+@{class:CelerityStaticResourceResponse} also manages some Javelin information,
+and @{function:celerity_generate_unique_node_id} uses this metadata to provide
+a better uniqueness guarantee when generating unique node IDs.
diff --git a/src/infrastructure/celerity/api/CelerityAPI.php b/src/infrastructure/celerity/api/CelerityAPI.php
index bf9064f44b..16f7b640ba 100644
--- a/src/infrastructure/celerity/api/CelerityAPI.php
+++ b/src/infrastructure/celerity/api/CelerityAPI.php
@@ -16,6 +16,12 @@
* limitations under the License.
*/
+/**
+ * Indirection layer which provisions for a terrifying future where we need to
+ * build multiple resource responses per page.
+ *
+ * @group celerity
+ */
final class CelerityAPI {
private static $response;
@@ -27,18 +33,4 @@ final class CelerityAPI {
return self::$response;
}
-}
-
-function require_celerity_resource($symbol) {
- $response = CelerityAPI::getStaticResourceResponse();
- $response->requireResource($symbol);
-}
-
-function celerity_generate_unique_node_id() {
- static $uniq = 0;
- $response = CelerityAPI::getStaticResourceResponse();
- $block = $response->getMetadataBlock();
-
- return 'UQ'.$block.'_'.($uniq++);
-}
-
+}
\ No newline at end of file
diff --git a/src/infrastructure/celerity/api/__init__.php b/src/infrastructure/celerity/api/__init__.php
index 762576517c..144af866b1 100644
--- a/src/infrastructure/celerity/api/__init__.php
+++ b/src/infrastructure/celerity/api/__init__.php
@@ -10,3 +10,4 @@ phutil_require_module('phabricator', 'infrastructure/celerity/response');
phutil_require_source('CelerityAPI.php');
+phutil_require_source('utils.php');
diff --git a/src/infrastructure/celerity/api/utils.php b/src/infrastructure/celerity/api/utils.php
new file mode 100644
index 0000000000..ed82dbc9ce
--- /dev/null
+++ b/src/infrastructure/celerity/api/utils.php
@@ -0,0 +1,59 @@
+requireResource($symbol);
+}
+
+
+/**
+ * Generate a node ID which is guaranteed to be unique for the current page,
+ * even across Ajax requests. You should use this method to generate IDs for
+ * nodes which require a uniqueness guarantee.
+ *
+ * @return string A string appropriate for use as an 'id' attribute on a DOM
+ * node. It is guaranteed to be unique for the current page, even
+ * if the current request is a subsequent Ajax request.
+ *
+ * @group celerity
+ */
+function celerity_generate_unique_node_id() {
+ static $uniq = 0;
+ $response = CelerityAPI::getStaticResourceResponse();
+ $block = $response->getMetadataBlock();
+
+ return 'UQ'.$block.'_'.($uniq++);
+}
+
diff --git a/src/infrastructure/celerity/controller/CelerityResourceController.php b/src/infrastructure/celerity/controller/CelerityResourceController.php
index 2b09c7899e..0fe43c64ee 100644
--- a/src/infrastructure/celerity/controller/CelerityResourceController.php
+++ b/src/infrastructure/celerity/controller/CelerityResourceController.php
@@ -16,6 +16,13 @@
* limitations under the License.
*/
+/**
+ * Delivers CSS and JS resources to the browser. This controller handles all
+ * ##/res/## requests, and manages caching, package construction, and resource
+ * preprocessing.
+ *
+ * @group celerity
+ */
class CelerityResourceController extends AphrontController {
private $path;
diff --git a/src/infrastructure/celerity/map/CelerityResourceMap.php b/src/infrastructure/celerity/map/CelerityResourceMap.php
index 2dc0be4465..59fff93f34 100644
--- a/src/infrastructure/celerity/map/CelerityResourceMap.php
+++ b/src/infrastructure/celerity/map/CelerityResourceMap.php
@@ -16,6 +16,14 @@
* limitations under the License.
*/
+/**
+ * Interface to the static resource map, which is a graph of available
+ * 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;
@@ -124,9 +132,3 @@ final class CelerityResourceMap {
}
}
-
-function celerity_register_resource_map(array $map, array $package_map) {
- $instance = CelerityResourceMap::getInstance();
- $instance->setResourceMap($map);
- $instance->setPackageMap($package_map);
-}
diff --git a/src/infrastructure/celerity/map/__init__.php b/src/infrastructure/celerity/map/__init__.php
index d34da6d1be..889b007a93 100644
--- a/src/infrastructure/celerity/map/__init__.php
+++ b/src/infrastructure/celerity/map/__init__.php
@@ -11,3 +11,4 @@ phutil_require_module('phutil', 'utils');
phutil_require_source('CelerityResourceMap.php');
+phutil_require_source('utils.php');
diff --git a/src/infrastructure/celerity/map/utils.php b/src/infrastructure/celerity/map/utils.php
new file mode 100644
index 0000000000..93c5e05653
--- /dev/null
+++ b/src/infrastructure/celerity/map/utils.php
@@ -0,0 +1,29 @@
+setResourceMap($map);
+ $instance->setPackageMap($package_map);
+}
diff --git a/src/infrastructure/celerity/response/CelerityStaticResourceResponse.php b/src/infrastructure/celerity/response/CelerityStaticResourceResponse.php
index 8ef6be945c..4331df11cc 100644
--- a/src/infrastructure/celerity/response/CelerityStaticResourceResponse.php
+++ b/src/infrastructure/celerity/response/CelerityStaticResourceResponse.php
@@ -16,6 +16,13 @@
* limitations under the License.
*/
+/**
+ * Tracks and resolves dependencies the page declares with
+ * @{function:require_celerity_resource}, and then builds appropriate HTML or
+ * Ajax responses.
+ *
+ * @group celerity
+ */
final class CelerityStaticResourceResponse {
private $symbols = array();