diff --git a/.codecov.yml b/.codecov.yml index f9f4b3c..fea7da7 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -2,3 +2,4 @@ coverage: range: "0...100" ignore: - "Tests/" + - ".build/" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4eaaf58..abc30db 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,26 +1,29 @@ name: CI on: -- push + push: + branches: + - master + pull_request: jobs: xenial: container: - image: vapor/swift:5.1-xenial + image: vapor/swift:5.2-xenial runs-on: ubuntu-latest steps: - uses: actions/checkout@master - run: swift test --enable-test-discovery --enable-code-coverage bionic: container: - image: vapor/swift:5.1-bionic + image: vapor/swift:5.2-bionic runs-on: ubuntu-latest steps: - uses: actions/checkout@master - name: Run Bionic Tests run: swift test --enable-test-discovery --enable-code-coverage - name: Setup container for codecov upload - run: apt-get update && apt-get install curl + run: apt-get update && apt-get install -y curl - name: Process coverage file - run: llvm-cov show .build/x86_64-unknown-linux/debug/LeafMarkdownPackageTests.xctest -instr-profile=.build/x86_64-unknown-linux/debug/codecov/default.profdata > coverage.txt + run: llvm-cov show .build/x86_64-unknown-linux-gnu/debug/LeafMarkdownPackageTests.xctest -instr-profile=.build/debug/codecov/default.profdata > coverage.txt - name: Upload code coverage uses: codecov/codecov-action@v1 with: diff --git a/Package.swift b/Package.swift index 603aa39..e0a8237 100644 --- a/Package.swift +++ b/Package.swift @@ -1,19 +1,27 @@ -// swift-tools-version:5.1 +// swift-tools-version:5.2 import PackageDescription let package = Package( name: "LeafMarkdown", + platforms: [ + .macOS(.v10_15) + ], products: [ .library(name: "LeafMarkdown", targets: ["LeafMarkdown"]), ], dependencies: [ - .package(url: "https://github.com/vapor/template-kit.git", from: "1.4.0"), - .package(url: "https://github.com/vapor-community/markdown.git", from: "0.6.1"), - .package(url: "https://github.com/vapor/leaf.git", from: "3.0.0"), + .package(name: "SwiftMarkdown", url: "https://github.com/vapor-community/markdown.git", from: "0.6.1"), + .package(url: "https://github.com/vapor/leaf-kit.git", from: "1.0.0"), ], targets: [ - .target(name: "LeafMarkdown", dependencies: ["TemplateKit", "SwiftMarkdown"]), - .testTarget(name: "LeafMarkdownTests", dependencies: ["LeafMarkdown", "Leaf"]), + .target(name: "LeafMarkdown", dependencies: [ + .product(name: "LeafKit", package: "leaf-kit"), + .product(name: "SwiftMarkdown", package: "SwiftMarkdown"), + ]), + .testTarget(name: "LeafMarkdownTests", dependencies: [ + .target(name: "LeafMarkdown"), + .product(name: "LeafKit", package: "leaf-kit"), + ]), ] ) diff --git a/Package@swift-4.swift b/Package@swift-4.swift deleted file mode 100644 index 0553068..0000000 --- a/Package@swift-4.swift +++ /dev/null @@ -1,19 +0,0 @@ -// swift-tools-version:4.0 - -import PackageDescription - -let package = Package( - name: "LeafMarkdown", - products: [ - .library(name: "LeafMarkdown", targets: ["LeafMarkdown"]), - ], - dependencies: [ - .package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"), - .package(url: "https://github.com/vapor/leaf.git", from: "3.0.0"), - .package(url: "https://github.com/vapor-community/markdown.git", .upToNextMajor(from: "0.4.0")), - ], - targets: [ - .target(name: "LeafMarkdown", dependencies: ["Vapor", "Leaf", "SwiftMarkdown"]), - .testTarget(name: "LeafMarkdownTests", dependencies: ["LeafMarkdown"]), - ] -) diff --git a/README.md b/README.md index f416aa9..f74b79f 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # Leaf Markdown -[![Language](https://img.shields.io/badge/Swift-5.1-brightgreen.svg)](http://swift.org) -[![Build Status](https://github.com/vapor-community/leaf-markdown/workflows/CI/badge.svg?branch=master)](https://github.com/vapor-community/leaf-markdown/actions) -[![codecov](https://codecov.io/gh/vapor-community/leaf-markdown/branch/master/graph/badge.svg)](https://codecov.io/gh/vapor-community/leaf-markdown) -[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/vapor-community/leaf-markdown/master/LICENSE) +[![Language](https://img.shields.io/badge/Swift-5.2-brightgreen.svg)](http://swift.org) +[![Build Status](https://github.com/vapor-community/leaf-markdown/workflows/CI/badge.svg?branch=main)](https://github.com/vapor-community/leaf-markdown/actions) +[![codecov](https://codecov.io/gh/vapor-community/leaf-markdown/branch/main/graph/badge.svg)](https://codecov.io/gh/vapor-community/leaf-markdown) +[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/vapor-community/leaf-markdown/main/LICENSE) -A Markdown renderer for Vapor and Leaf. This uses the [Vapor Markdown](https://github.com/vapor/markdown) package to wrap [cmark](https://github.com/jgm/cmark) (though a [fork](https://github.com/brokenhandsio/cmark-gfm) is used to make it work with Swift PM), so it understands [Common Mark](http://commonmark.org). A quick reference guide for Common Mark can be found [here](http://commonmark.org/help/). It also supports [Github Flavored Markdown](https://guides.github.com/features/mastering-markdown/). +A Markdown renderer for Vapor and Leaf. This uses the [Vapor Markdown](https://github.com/vapor/markdown) package to wrap [cmark](https://github.com/github/cmark-gfm) (though a [fork](https://github.com/brokenhandsio/cmark-gfm) is used to make it work with Swift PM), so it understands [Common Mark](http://commonmark.org). A quick reference guide for Common Mark can be found [here](http://commonmark.org/help/). It also supports [Github Flavored Markdown](https://guides.github.com/features/mastering-markdown/). ## Usage @@ -32,21 +32,29 @@ Add Leaf Markdown as a dependency in your `Package.swift` file: ```swift dependencies: [ ..., - .package(url: "https://github.com/vapor-community/leaf-markdown.git", .upToNextMajor(from: "2.0.0")) + .package(name: "LeafMarkdown", url: "https://github.com/vapor-community/leaf-markdown.git", .upToNextMajor(from: "3.0.0")), ] ``` +Then add the dependency to your target: + +```swift +.target( + name: "App", + dependencies: [ + // ... + "LeafMarkdown" + ], + // ... +) +``` + ### Register with Leaf -To add the tag to Leaf, add it to your `LeafTagConfig`: +Register the tag with Leaf so Leaf knows about it: ```swift -try services.register(LeafProvider()) -var tags = LeafTagConfig.default() -tags.use(Markdown(), as: "markdown") -services.register(tags) +app.leaf.tags["markdown"] = Markdown() ``` -**Note:** it's important that you register the `LeafProvider` first otherwise this will override your `LeafTagConfig`. - Don't forget to import LeafMarkdown in the file you register the tag with `import LeafMarkdown`. diff --git a/Sources/LeafMarkdown/Tag.swift b/Sources/LeafMarkdown/Tag.swift index 6c4b736..45ff2de 100644 --- a/Sources/LeafMarkdown/Tag.swift +++ b/Sources/LeafMarkdown/Tag.swift @@ -1,42 +1,34 @@ -import TemplateKit +import LeafKit import SwiftMarkdown -public final class Markdown: TagRenderer { - +public struct Markdown: LeafTag { public enum Error: Swift.Error { - case invalidArgument(TemplateData?) + case invalidArgument(LeafData?) } - - public let name = "markdown" - + private let options: MarkdownOptions? - + public init(options: MarkdownOptions? = nil) { self.options = options } - - public func render(tag: TagContext) throws -> Future { - + + public func render(_ ctx: LeafContext) throws -> LeafData { var markdown = "" - - if let markdownArgument = tag.parameters.first, !markdownArgument.isNull { + + if let markdownArgument = ctx.parameters.first, !markdownArgument.isNil { guard let markdownArgumentValue = markdownArgument.string else { - throw Error.invalidArgument(tag.parameters.first) + throw Error.invalidArgument(ctx.parameters.first) } markdown = markdownArgumentValue } - let markdownHTML: String = try { - if let options = options { - return try markdownToHTML(markdown, options: options) - } else { - return try markdownToHTML(markdown) - } - }() - - return Future.map(on: tag) { - .string(markdownHTML) + let markdownHTML: String + if let options = options { + markdownHTML = try markdownToHTML(markdown, options: options) + } else { + markdownHTML = try markdownToHTML(markdown) } - } + return .string(markdownHTML) + } } diff --git a/Tests/LeafMarkdownTests/LeafTests.swift b/Tests/LeafMarkdownTests/LeafTests.swift index f1e83fb..302563d 100644 --- a/Tests/LeafMarkdownTests/LeafTests.swift +++ b/Tests/LeafMarkdownTests/LeafTests.swift @@ -1,73 +1,70 @@ import XCTest -@testable import Leaf +@testable import LeafKit import LeafMarkdown +import NIO -class LeafTests: XCTestCase { - +class MarkdownTests: XCTestCase { // MARK: - Properties var renderer: LeafRenderer! - let template = "#markdown(data)" + var ast: [Syntax]! + var markdownTag: Markdown! // MARK: - Overrides - override func setUp() { - let queue = EmbeddedEventLoop() - let container = BasicContainer(config: .init(), environment: .testing, services: .init(), on: queue) - let tag = Markdown() - var leafTagConfig = LeafTagConfig.default() - leafTagConfig.use(tag, as: tag.name) - self.renderer = LeafRenderer(config: LeafConfig(tags: leafTagConfig, viewsDir: "", shouldCache: false), using: container) + override func setUpWithError() throws { + var lexer = LeafLexer(name: "markdowntest", template: "#markdown(data)") + let tokens = try lexer.lex() + var parser = LeafParser(name: "markdowntest", tokens: tokens) + ast = try parser.parse() + markdownTag = Markdown() + } + + // MARK: - Helper + func render(context: [String: LeafData]) throws -> String { + var serializer = LeafSerializer( + ast: ast, + context: context, + tags: ["markdown": markdownTag] + ) + let view = try serializer.serialize() + return view.getString(at: view.readerIndex, length: view.readableBytes) ?? "" } // MARK: - Tests func testRunTag() throws { let inputMarkdown = "# This is a test\n\nWe have some text in a tag" - let data = TemplateData.dictionary(["data": .string(inputMarkdown)]) let expectedHtml = "

This is a test

\n

We have some text in a tag

\n" - - let result = try renderer.render(template: template.data(using: .utf8)!, data).wait() - let resultString = String(data: result.data, encoding: .utf8)! + let resultString = try render(context: ["data": .string(inputMarkdown)]) XCTAssertEqual(resultString, expectedHtml) } func testNilParameterDoesNotCrashLeaf() throws { - let data = TemplateData.dictionary(["data": .null]) let expectedHtml = "" - - let result = try renderer.render(template: template.data(using: .utf8)!, data).wait() - let resultString = String(data: result.data, encoding: .utf8)! + let resultString = try render(context: ["data": .trueNil]) XCTAssertEqual(resultString, expectedHtml) } func testStripHtml() throws { let inputMarkdown = "
" - let data = TemplateData.dictionary(["data": .string(inputMarkdown)]) let expectedHtml = "\n" - let result = try renderer.render(template: template.data(using: .utf8)!, data).wait() - let resultString = String(data: result.data, encoding: .utf8)! + let resultString = try render(context: ["data": .string(inputMarkdown)]) XCTAssertEqual(resultString, expectedHtml) + } + func testRejectBadData() throws { + XCTAssertThrowsError(try render(context: ["data": .dictionary(["something": .string("somethingelese")])])) } func testDoNotStripHtml() throws { - - let queue = EmbeddedEventLoop() - let container = BasicContainer(config: .init(), environment: .testing, services: .init(), on: queue) - let tag = Markdown(options: [.unsafe]) - var leafTagConfig = LeafTagConfig.default() - leafTagConfig.use(tag, as: tag.name) - let renderer = LeafRenderer(config: LeafConfig(tags: leafTagConfig, viewsDir: "", shouldCache: false), - using: container) + markdownTag = Markdown(options: [.unsafe]) let inputMarkdown = "
" - let data = TemplateData.dictionary(["data": .string(inputMarkdown)]) let expectedHtml = "
\n" - let result = try renderer.render(template: template.data(using: .utf8)!, data).wait() - let resultString = String(data: result.data, encoding: .utf8)! + let resultString = try render(context: ["data": .string(inputMarkdown)]) XCTAssertEqual(resultString, expectedHtml) } }