Skip to content

Swift-Liquid 1.3.0

Choose a tag to compare

@ThorFuchs ThorFuchs released this 15 Oct 11:44

Swift-Liquid 1.3.0

Swift-Liquid 1.3 adds bounded partial-dependency inspection to the dependency-free
Shopify Liquid engine. Inspect a supplied template bundle before rendering it,
find missing literal references and cycles, and identify unresolved dynamic
includes. The library and CLI share the existing AST inspection authority.
Existing 1.0–1.2 rendering, filters, diagnostics, inspection and metrics APIs keep
their behavior.

Requires Swift tools/compiler 6.4 or later, in Swift language mode 6.

Understand an explicit template bundle

import Liquid

let engine = LiquidEngine()
let source = "{% include 'card' %}{% include selected %}"
let partials = ["card.liquid": "{{ title | escape }}{% include 'badge' %}"]
let graph = try engine.inspectDependencies(
    template: source, templates: partials, syntaxMode: .strict2
)
assert(graph.nodes.map(\.id) == [.root, .partial("card.liquid")])
assert(graph.missingReferences.map(\.requestedName) == ["badge"])
assert(graph.dynamicReferences == [.root])

The source entry point parses every supplied source under the chosen mode.
The parsed-template overload reuses each template's original parsing:

let root = try LiquidTemplate(source, syntaxMode: .strict2)
let parsed = try partials.mapValues { try LiquidTemplate($0, syntaxMode: .strict2) }
let reused = try engine.inspectDependencies(root, templates: parsed)
assert(reused == graph)

Results, node identities, edges and capacity profiles have checked Sendable
conformance. Every operation owns its worklists and sets. Inspection performs no
I/O, context lookup, expression evaluation or filter invocation.

Nodes contain the root and literal-reachable partials, each with its existing
single-template summary. Edges retain the exact requested spelling and actual
supplied key. Lookup follows rendering: the requested key first, then that key
plus .liquid, using Swift String dictionary equality. The result preserves exact
UTF-8 spellings and deterministic order; the root remains distinct from every
partial name. Repeated requests share an edge, and shared dependencies expand once.

Cycles are deterministic strongly connected groups, including self-loops. Both
traversal and cycle detection use iterative worklists. Dynamic include names
remain unresolved; dynamic render names retain their existing syntax rejection.
All retained branches contribute. Findings describe syntax and supplied names;
they do not prove execution paths, required context or render success.

Bound the work

LiquidDependencyLimits defaults to 4,096 supplied templates including the root,
262,144 aggregate supplied-key bytes and 65,536 retained literal edges, including
missing references. The root and every supplied partial share the engine's source
capacity; each AST must pass its syntax-depth limit. Empty and unused sources
still spend admission capacity, and unused malformed sources fail the operation.

Rendering capacities are not consumed. Invalid profiles, syntax and capacity
failures preserve their typed errors; cancellation remains CancellationError.
Failure returns no partial graph. These are logical capacities, not memory or
wall-clock guarantees. See the complete dependency contract
for charging, ordering and failure rules.

Analyze from the command line

liquid analyze page.liquid --dependencies --partials ./partials --format json

--dependencies opts into the graph report. The partial directory is optional;
omitting it supplies an empty map. The CLI admits cumulative source, template
count and name bytes before parsing the bundle, skips hidden entries and child
symlinks, checks containment and rejects ambiguous discovered keys.

Dependency reports use separate JSON schema 1 with kind: "liquid.dependencies",
node identities, edges, missing/dynamic references and cycle groups. Successful
findings exit 0, including missing references or cycles. Operational failures
emit no graph, write diagnostics to stderr and exit 1. Consumers choose their own
rejection policy. Ordinary analyze and its inspection schema 1 are unchanged;
rendering and metrics schema 1 are unchanged.

The standalone qualification tool validates actual CLI output and negative
vectors with published Swift-JSON 1.3.0 JSON and JSONSchema. This dependency
belongs only to tooling. Liquid retains its empty root dependency graph and
existing deployment minimums. Schema validity checks report shape; API and CLI
regressions separately check graph relationships and exact-spelling semantics.

Compatibility and qualification

  • Preserve all 116 published 1.2 public declarations and all 1.0–1.2 consumers.
    DocC exposes 148 public symbols, including the additive dependency API.
  • Retain fixture bytes, checksums, licenses, parser-mode metadata and reference
    pins. The Ruby reference accounts for 2,032 core checks: 1,502 successful renders,
    337 parsed grammar cases, 132 expected errors, 26 recovered errors and 35
    supported-year boundaries. Host extensions retain 15 separate paired cases.
    Dependency inspection adds no Shopify render-conformance cases.
  • Qualify 65 test functions in debug and release, five public consumers, the
    existing 60 CLI cases and 24 dependency CLI cases, plus six positive and nineteen
    negative Swift-JSON schema vectors. AddressSanitizer covers the full suite;
    ThreadSanitizer covers five mixed concurrency cases.
  • Canonically equivalent filenames cannot coexist on the qualification macOS
    filesystem, so that duplicate-path CLI vector is unavailable there and is not
    counted as passed. Public API regressions cover canonical lookup and exact
    spelling. The CLI vector runs when the filesystem supports both names.
  • Compile all four declared simulator SDKs, compile DocC and rebuild a reproducible
    source archive from a clean extraction. Runtime qualification covers macOS;
    simulator compilation does not certify device, Linux or WASI execution.

Measured against 1.2.0

Matched measurements use immutable 1.2.0 commit
8676da8059e71905632c233cffb438f8edbf51d2, its unchanged benchmark harness and
manifest, and all 26 common ordinary-render, measured-render and inspection
records. A separate same-candidate phase retains 24 enabled/disabled metrics
comparisons across throwing and diagnostic policies. Both phases use three serial
ABBA blocks, six runs per side, ten warmup samples and 25 measured samples of
100 repetitions. All binaries build before timing; outputs, checksums, individual
samples and environment receipts are retained. Every threshold flag is reviewed.

Four additional source/parsed diamond and cycle records measure graph construction
separately, with ten warmups and 25 samples of 20 repetitions. Full result equality
and checksum audits are inside their timing boundary. The previous release has
no graph operation for a direct comparison. The qualification archive carries
measurements and their bound review; this release makes no general speedup,
zero-overhead or process-memory claim. See performance methodology.

Upgrade and downloads

Select from: "1.3.0" and retain the Liquid product. Existing
applications need no migration. Opt into dependency inspection when a host or
tool needs it; parsed template reuse and ordinary rendering keep their APIs.

The release includes:

  • Swift-Liquid-source.tar.gz: tracked source verified by a clean rebuild.
  • Liquid.doccarchive.tar.gz: compiled API reference and guides.
  • Swift-Liquid-qualification.tar.gz: gate receipts, logs, source inventory,
    matched and graph measurements, and reviewed benchmark evidence.
  • SHA256SUMS: SHA-256 checksums for the three archives.

After downloading the published assets together, verify them with
shasum -a 256 -c SHA256SUMS before extraction. See the CLI reference,
dependency report schema,
application reuse and errors and recovery.

Qualification: candidate 9963591f18ef8aa1d1a8d9cd9bcc5508c82b5d4e, source tree bc30f14d27c9d29255c8153386cdf512beadc945, tested as 9963591f18ef8aa1d1a8d9cd9bcc5508c82b5d4e. Exact-source CI evidence.

Final measured qualification

The final CI comparison retains all 26 common records against immutable 1.2.0,
with median changes from −9.05% to +9.00% and no 10% review flags. Qualification
identified and corrected repeated native-string custom-filter bridging slowness;
custom-filter template reuse is +0.03% in the final comparison. Three separate
same-candidate opt-in metrics comparisons show +11.45% to +15.89% higher latency.
The qualification archive retains all eight successful comparisons, every flag
and sample, the failed pre-timing disk attempt, and the full bound review. These
limited workloads establish no general speedup, zero-overhead or portable latency
claim. The public SwiftPM 1.3.0 consumer, annotated tag and hosted DocC contents
are also verified in the qualification evidence.