|
|
@@ -26,6 +26,16 @@ namespace Statum.Tools {
|
|
|
private Dictionary<string, Xml.Doc*> templates_by_name = new Dictionary<string, Xml.Doc*>();
|
|
|
private Dictionary<string, Xml.Doc*> fragments_by_name = new Dictionary<string, Xml.Doc*>();
|
|
|
|
|
|
+ /**
|
|
|
+ * The Statum client ships as four modules with a strict load order
|
|
|
+ * (core first: every later module registers into the seams core
|
|
|
+ * installs). A legacy single-file `pstm-res-src="statum.js"` reference
|
|
|
+ * expands at build time into exactly these four tags.
|
|
|
+ */
|
|
|
+ private const string[] CLIENT_MODULE_ORDER = {
|
|
|
+ "statum-core.js", "statum-document.js", "statum-markdown.js", "statum-forms.js"
|
|
|
+ };
|
|
|
+
|
|
|
private const OptionEntry[] options = {
|
|
|
{ "output", 'o', 0, OptionArg.FILENAME, ref output_file, "Output Vala file name", "FILE" },
|
|
|
{ "ns", '\0', 0, OptionArg.STRING, ref namespace_name, "Namespace for the generated class", "NAMESPACE" },
|
|
|
@@ -85,6 +95,7 @@ namespace Statum.Tools {
|
|
|
|
|
|
doc = compose_templates(doc);
|
|
|
inline_fragments(doc);
|
|
|
+ expand_legacy_client(doc, page_file);
|
|
|
rewrite_resources(doc);
|
|
|
|
|
|
var variant = build_variant(doc);
|
|
|
@@ -398,6 +409,124 @@ namespace Statum.Tools {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Expands a legacy single-file client reference into the four ordered
|
|
|
+ * module tags.
|
|
|
+ *
|
|
|
+ * `<script pstm-res-src="statum.js">` (the pre-split client, no longer
|
|
|
+ * an embedded resource) becomes one `<script src="/_statum/resource/
|
|
|
+ * statum-<mod>.js">` tag per module, in canonical load order. Later
|
|
|
+ * duplicate `statum.js` references on the same page are dropped, and
|
|
|
+ * explicit `pstm-res-src="statum-<mod>.js"` references the expansion
|
|
|
+ * already covers are removed (subsumed) so every module appears exactly
|
|
|
+ * once. Pages referencing only module files (or only plain `src="…"`
|
|
|
+ * scripts, which are already final) are left entirely alone.
|
|
|
+ *
|
|
|
+ * Runs on the composed document before {@link rewrite_resources}: the
|
|
|
+ * inserted tags use plain `src` attributes, so the generic rewrite and
|
|
|
+ * `strip_pstm` both leave them alone.
|
|
|
+ */
|
|
|
+ private void expand_legacy_client(Xml.Doc* doc, string page_file) {
|
|
|
+ var root = doc->get_root_element();
|
|
|
+ if (root == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Xml.Node* legacy = null;
|
|
|
+ var refs = new ClientRefs();
|
|
|
+ collect_client_refs(root, refs);
|
|
|
+ legacy = refs.legacy;
|
|
|
+ if (legacy == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var parent = legacy->parent;
|
|
|
+ if (parent == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (var module in CLIENT_MODULE_ORDER) {
|
|
|
+ var tag = ((!)parent)->new_child(null, "script", null);
|
|
|
+ tag->unlink();
|
|
|
+ // Script tags carrying pstm-res-src carry no other attributes
|
|
|
+ // today; copy any extras onto all four for good measure.
|
|
|
+ copy_non_pstm_attrs(legacy, tag);
|
|
|
+ tag->set_prop("src", @"/_statum/resource/$module");
|
|
|
+ legacy->add_prev_sibling(tag);
|
|
|
+ }
|
|
|
+
|
|
|
+ string[] subsumed = new string[0];
|
|
|
+ foreach (var node in refs.modules) {
|
|
|
+ var name = node->get_prop("pstm-res-src");
|
|
|
+ if (name != null) {
|
|
|
+ subsumed += (!)name;
|
|
|
+ }
|
|
|
+ node->unlink();
|
|
|
+ }
|
|
|
+ legacy->unlink();
|
|
|
+
|
|
|
+ // Exactly one build warning per page, naming the page and the
|
|
|
+ // expansion.
|
|
|
+ var page = page_name(page_file);
|
|
|
+ stderr.printf("warning: %s: expanding legacy pstm-res-src=\"statum.js\" into %s",
|
|
|
+ page, string.joinv(", ", CLIENT_MODULE_ORDER));
|
|
|
+ if (subsumed.length > 0) {
|
|
|
+ stderr.printf("; subsumed explicit module tag(s): %s", string.joinv(", ", subsumed));
|
|
|
+ }
|
|
|
+ if (refs.duplicates > 0) {
|
|
|
+ stderr.printf("; dropped %d duplicate statum.js reference(s)", refs.duplicates);
|
|
|
+ }
|
|
|
+ stderr.printf("\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Collects the client script references of a composed document: the
|
|
|
+ * first `<script pstm-res-src="statum.js">` (if any), every explicit
|
|
|
+ * module `pstm-res-src="statum-<mod>.js"` reference, and the count of
|
|
|
+ * further `statum.js` duplicates.
|
|
|
+ */
|
|
|
+ private void collect_client_refs(Xml.Node* node, ClientRefs refs) {
|
|
|
+ if (!is_element(node)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if ((string) node->name == "script") {
|
|
|
+ var res = node->get_prop("pstm-res-src");
|
|
|
+ if (res != null) {
|
|
|
+ if ((!)res == "statum.js") {
|
|
|
+ if (refs.legacy == null) {
|
|
|
+ refs.legacy = node;
|
|
|
+ } else {
|
|
|
+ refs.duplicates++;
|
|
|
+ }
|
|
|
+ } else if (is_client_module((!)res)) {
|
|
|
+ refs.modules += node;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (var child = node->children; child != null; child = child->next) {
|
|
|
+ collect_client_refs(child, refs);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** The page's short name, as warnings name it (e.g. `home`). */
|
|
|
+ private static string page_name(string page_file) {
|
|
|
+ var page = Path.get_basename(page_file);
|
|
|
+ var dot = page.last_index_of(".");
|
|
|
+ if (dot > 0) {
|
|
|
+ page = page.substring(0, dot);
|
|
|
+ }
|
|
|
+ return page;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static bool is_client_module(string name) {
|
|
|
+ foreach (var module in CLIENT_MODULE_ORDER) {
|
|
|
+ if (name == module) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
private void rewrite_resources_in(Xml.Node* node) {
|
|
|
if (!is_element(node)) {
|
|
|
return;
|
|
|
@@ -838,6 +967,13 @@ namespace Statum.Tools {
|
|
|
// Model
|
|
|
// ------------------------------------------------------------------
|
|
|
|
|
|
+ /** Scratch state for {@link collect_client_refs}. */
|
|
|
+ private class ClientRefs : Object {
|
|
|
+ public Xml.Node* legacy = null;
|
|
|
+ public Xml.Node*[] modules = new Xml.Node*[0];
|
|
|
+ public int duplicates = 0;
|
|
|
+ }
|
|
|
+
|
|
|
private class VariantData : Object {
|
|
|
public uint8[] identity;
|
|
|
public uint8[]? gzip;
|