Skip to content

Releases: vapor/leaf-kit

LeafKit 1.0.0

12 Nov 09:47
0f0866e
Compare
Choose a tag to compare

LeafKit 1.0.0

1.0.0 Zeta 1

08 Nov 16:21
595434b
Compare
Choose a tag to compare
1.0.0 Zeta 1 Pre-release
Pre-release

This release reverses the Tau changes and reflects what (should) be the final release for LeafKit 1.0.0 barring any glaring bugs. If you want to keep using Tau, we strongly encourage you to do so! You can find it (currently) in the fork here

Parse & Serialize Bug Fixes

01 Nov 22:34
8b965e6
Compare
Choose a tag to compare
Pre-release
This patch was authored and released by @tdotclare.
  • Properly respect invariant flag on entities during parsing to avoid pre-resolving
  • Correct behavior on nested inline variable/define resolution
  • Correct behavior of declaring variables referencing previous stack definition
  • Improved error handling on define style mismatches in resolution

LeafKit 1.0.0-tau

01 Nov 06:16
61d6928
Compare
Choose a tag to compare
LeafKit 1.0.0-tau Pre-release
Pre-release

tau is the final prerelease of LeafKit 1.0 and Leaf4.

This is a massive rebuild from the previous prerelease version and entirely replaces the architecture for extending the language; as such, there is no direct transition path for converting LeafTag objects to the new architecture directly.

Migration documents are coming soon to address how that functionality has improved and changed

If you currently use the Leaf4 pre-release versions, pin to the final rc branch in your Swift Package Manifest to avoid updating to this state if you are not ready to transition:

dependencies: [
  .package(url: "https://github.com/vapor/leaf-kit.git", .exact("1.0.0-rc.1.17")),
  .package(url: "https://github.com/vapor/leaf.git", .exact("4.0.0-rc.1.4"))
]

Leaf4 is a dynamic language templating engine for (and inspired by) Swift with a unique hybrid design to allow significant extensibility, customization, performance, and optimizations for a broad range of applications. Leaf templates function primarily as the View component of Model-View-Controller architectures.

As the successor to Leaf3, it greatly expands the language's capabilities and introduces significant changes that are oriented towards: simplfying integration of Leaf into applications; broader use beyond web-templating; robust handling of compiling templates; and improved, more powerful, and safer extensibility of templates at runtime.

LeafKit is the core architecture of Leaf4

Leaf is the bindings of LeafKit to Vapor and Vapor-specific configuration.

Internal Bug Fix

08 Jul 20:09
a3623bd
Compare
Choose a tag to compare
Internal Bug Fix Pre-release
Pre-release
This patch was authored and released by @tdotclare.

Internal Only

Fixed malformed assert checks in LeafConfiguration

LeafConfiguration Encoding and Formatting Options

07 Jul 11:31
ec9593f
Compare
Choose a tag to compare
This patch was authored and released by @tdotclare.

This release adds configuration methods for setting the default presentation output of data in rendered Leaf templates.

These options are all settable before LeafKit starts running.

Property Options

Stored property options in a LeafConfiguration may be used in various ways by a LeafRenderer which the configuration object was for, and changes to them after the object was provided to a specific LeafRenderer will have no affect.

.rootDirectory: String   // The default file directory used for file-system based `LeafSource`s                  

Static Options

Static options on LeafConfiguration are effectively constant once any LeafRenderer has been instantiated and attempts to change them will assert in Debug and silently fail in Release to prevent inconsistent behavior.

 // The global tag indicator for LeafKit
.tagIndicator: Character == "#"
 // Encoding used when a template is serialized
.encoding: String.Encoding == .utf8

// Formatters for converting the base internal data types to Strings for serialization
.boolFormatter: (Bool) -> String = { $0.description }     // Bool.description
.intFormatter: (Int) -> String = { $0.description }       // Int.description
.doubleFormatter: (Double) -> String = { $0.description } // Double.description
.nilFormatter: () -> String = { "" }                      // Empty string (Optional containing .none)
.voidFormatter: () -> String = { "" }                     // Empty string (Tag with no return value)
.stringFormatter: (String) -> String = { $0 }             // Identity return
.dataFormatter: (Data) -> String? =
        { String(data: $0, encoding: Self._encoding) }    // Data using .encoding

// Note: Array & Dictionaries elements will already have been converted to Strings

.arrayFormatter: ([String]) -> String =                   // Array: [element, ..., element]
        { "[\($0.map {"\"\($0)\""}.joined(separator: ", "))]" }
.dictFormatter: ([String: String]) -> String =            // Dictionary: [key: value, ..., key: value]
        { "[\($0.map { "\($0): \"\($1)\"" }.joined(separator: ", "))]" }

API Changes

  • Character.tagIndicator can no longer be directly set - it must be configured through LeafConfiguration

  • LeafData.NaturalType represents eight concrete Swift data types Leaf handles:

    • Instantiable data types: [Bool, Int, Double, String, Array, Dictionary, Data]
    • Non-instantiable: [Void]
  • LeafData static initializers from Swift data types now take Optional values:

    • If input is .none, the returned LeafData represents a Optional.none state of the specific type
    • .nil static initializer now requires a concrete type from LeafData.NaturalType be specified
  • LeafData objects present the following informational states:

    • celf : NaturalType of the object
    • isNil: whether the object contains .none
    • isCollection: if the object's type is array or dictionary.
    • isCastable(to type: NaturalType): whether the type is implicitly castable to a second type
    • isCoercible(to type: NaturalType): whether the type has an implicit one-way conersion path to the second type
    • NOTE both above methods are inherently true when the two types are the same type, or castable when asking if coercible.
    • hasUniformType: whether the object consists of one concrete type (true for all non-containers), false if it can be determined that it does not, and nil in unusual cases where an object is storing an internal dynamic data generator that returns a container itself.
    • uniformType: If the object can be determined to have a uniformType, returns that type or nil if not determinable.
  • LeafDataRepresentable adherence now requires .leafData return LeafData rather than LeafData?

    • Updated static initalizers mentioned above will automatically handle creating LeafData holding .none where otherwise a nil return would occur
    • Generic conformances for various Swift base types are updated to reflect this:
      • let invalidValue: Float80 = Float80.max
        let leafData = invalidValue.leafData
        // leafData now represents the equivalent of Double? containing nil
      • Default implementations for protocols FixedWidthInteger and BinaryFloatingPoint
      • Adherance via above for Bool, String, Int/Int8/Int32/Int64/UInt/UInt8/UInt16/UInt32/UInt64, Float/Double/Float80, Data, UUID (via String), Date (via Double), Array<LeafDataRepresentable>, Dictionary<String, LeafDataRepresentable>, Set<LeafDataRepresentable> via Array

Internal Only

Substantial internal changes to LeafData, LeafDataRepresentable Protocol, LeafDataStorage :

  • LeafData conversion between types is now explicitly handled by castable and coercible rules where underlying types are different. The default behaviors allow only implicit conversion via castable rules where a clearly established bi-directional rule exists between the two types.
  • LeafDataStorage signifcantly modified to allow above behaviors
    • Internal Lazy resolvable data generators must now state their concrete return type and whether they have variant return behavior; such generators will never be tested in any kind of comparison states to prevent side-effects - they will only be called when a template serializes.
    • Various state evaluation, data conversion, and related behaviors are moved internally to the enum definition to prevent confusing situations
  • Various internal changes reflecting the above modifications

Fixes

  • Throws an error if overflowing add/subtract on integer values

Fix Performance Regression

01 Jul 02:18
e84acaf
Compare
Choose a tag to compare
Pre-release
This patch was authored and released by @tdotclare.

This release fixes a performance regression from an internal testing function being used by public render() calls

Performance Improvements

30 Jun 16:21
89dd969
Compare
Choose a tag to compare
Pre-release
This patch was authored and released by @tdotclare.

This release significantly improves performance of reading raw template files

Internal Notes

Template source documents being parsed previously involved mutating an array copy of the template source; now scans a constant String copy and pre-allocates chunked reading method [Character] storage

Improved Rendering Performance

25 Jun 17:46
27724d0
Compare
Choose a tag to compare
Pre-release
This patch was authored and released by @tdotclare.

This release substantially improves the rendering performance of Leaf on typical calls where the template is fully resolved and cached.

Performance Comparison

  • Linear test - 10 flat templates, 1 million render calls against them
  • Random test - 10 flat templates (layer 3), 20 templates referencing one of the flat templates (layer 2), 100 templates referencing two random layer 2 templates (layer 1). 1 million render calls against a random one of the 130 total templates.
  • Each test was run 50 times on an 4GHz i7 Retina iMac

Linear

Branch Min Avg Max Avg Baseline Avg CPU Time CPU Baseline
1.0.0rc-1.13 4.27s 4.72s 5.14s 44.83% 1m 14s 40.72%
1.0.0rc-1.12 10.4s 10.52s 11.4s 100% 3m 1s 100%

Random

Branch Min Avg Max Avg Baseline Avg CPU Time CPU Baseline
1.0.0rc-1.13 4.43s 4.82s 5.17s 49.9% 1m 18s 45.3%
1.0.0rc-1.12 9s 9.66s 10.61s 100% 2m 51s 100%

NOTE this is purely a pipeline measurement - the templates used are lightweight and require near-zero time to serialize

Set Default File Extension for `NIOLeafFiles`

24 Jun 17:20
c772598
Compare
Choose a tag to compare
This patch was authored and released by @tdotclare.

This release adds an initialization parameter to NIOLeafFiles to allow setting the default extension to be used for Leaf files.

public struct NIOLeafFiles: LeafSource {
    ...
    /// Initialize `NIOLeafFiles` with a NIO file IO object, limit options, and sandbox/view dirs
    /// - Parameters:
    ///   - fileio: `NonBlockingFileIO` file object
    ///   - limits: Options for constraining which files may be read - see `NIOLeafFiles.Limit`
    ///   - sandboxDirectory: Full path of the lowest directory which may be escaped to
    ///   - viewDirectory: Full path of the default directory templates are relative to
    ///   - defaultExtension: The default extension inferred files will have (defaults to `leaf`)
    ///
    /// `viewDirectory` must be contained within (or overlap) `sandboxDirectory`
    public init(fileio: NonBlockingFileIO,
                limits: Limit = .default,
                sandboxDirectory: String = "/",
                viewDirectory: String = "/",
                defaultExtension: String = "leaf") {...}

Usage:

    // Use "leaf4" instead of "leaf" as the implied file extension
    let source: LeafSource = NIOLeafFiles(..., defaultExtension: "leaf4")

Internal Changes

  • Various documentation of public protocols/objects
  • General rearrangement of code for clarity