Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Prismic V2 format #17

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ PATH
GEM
remote: https://rubygems.org/
specs:
diffy (3.4.2)
json (2.7.1)
kramdown (2.4.0)
rexml
minitest (5.16.3)
minitest-match_json (0.2.0)
diffy
json
minitest (>= 4.0)
rake (13.0.6)
rexml (3.2.5)

Expand All @@ -19,6 +25,7 @@ PLATFORMS
DEPENDENCIES
kramdown-prismic!
minitest (~> 5.0)
minitest-match_json (~> 0.2.0)
rake (~> 13.0)

BUNDLED WITH
Expand Down
2 changes: 1 addition & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ require 'rake/testtask'

Rake::TestTask.new do |t|
t.libs << 'test'
t.test_files = FileList['test/*_test.rb']
t.test_files = FileList['test/**/*_test.rb']
t.verbose = true
end
23 changes: 22 additions & 1 deletion bin/html2prismic
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,26 @@

require 'kramdown-prismic'
require 'json'
require 'optparse'

print Kramdown::Document.new(ARGV[0], input: :html).to_prismic.to_json.to_s
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: html2prismic [options]"

opts.on("-fFORMAT", "--format=FORMAT", "Prismic format to use") do |format|
options[:format] = format&.strip&.downcase&.to_sym
end
end.parse!

document = Kramdown::Document.new(ARGV[0], input: :html)

result = case options.fetch(:format, :v1)
when :v1
document.to_prismic
when :v2
document.to_prismic_v2
else
print "Unknown Prismic fomat variant"
end

print result.to_json.to_s
25 changes: 24 additions & 1 deletion bin/markdown2prismic
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,28 @@

require 'kramdown-prismic'
require 'json'
require 'optparse'

print Kramdown::Document.new(ARGV[0], input: :markdown).to_prismic.to_json.to_s
begin
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: markdown2prismic [options]"

opts.on("-fFORMAT", "--format=FORMAT", "Prismic format to use") do |format|
options[:format] = format&.strip&.downcase&.to_sym
end
end.parse!

document = Kramdown::Document.new(ARGV[0], input: :markdown)

result = case options.fetch(:format, :v1)
when :v1
document.to_prismic
when :v2
document.to_prismic_v2
else
print "Unknown Prismic fomat variant"
end

print result.to_json.to_s
end
31 changes: 31 additions & 0 deletions bin/prismic2html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/usr/bin/env ruby

require 'kramdown-prismic'
require 'json'
require 'optparse'

begin
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: prismic2markdown [options]"

opts.on("-fFORMAT", "--format=FORMAT", "Prismic format to use") do |format|
options[:format] = format&.strip&.downcase&.to_sym
end
end.parse!

source = JSON.parse(ARGV[0], symbolize_names: true)

format = case options.fetch(:format, :v1)
when :v1
:prismic
when :v2
:prismic_v2
else
print "Unknown Prismic fomat variant"
end

document = Kramdown::Document.new(source, input: format)

print document.to_html
end
27 changes: 25 additions & 2 deletions bin/prismic2markdown
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,30 @@

require 'kramdown-prismic'
require 'json'
require 'optparse'

source = JSON.parse(ARGV[0], symbolize_names: true)
begin
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: prismic2markdown [options]"

print Kramdown::Document.new(source, input: :prismic).to_kramdown
opts.on("-fFORMAT", "--format=FORMAT", "Prismic format to use") do |format|
options[:format] = format&.strip&.downcase&.to_sym
end
end.parse!

source = JSON.parse(ARGV[0], symbolize_names: true)

format = case options.fetch(:format, :v1)
when :v1
:prismic
when :v2
:prismic_v2
else
print "Unknown Prismic fomat variant"
end

document = Kramdown::Document.new(source, input: format)

print document.to_kramdown
end
2 changes: 2 additions & 0 deletions kramdown-prismic.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Gem::Specification.new do |s|
s.license = 'MIT'

s.add_dependency 'kramdown', '>= 1', '< 3'

s.add_development_dependency 'minitest', '~> 5.0'
s.add_development_dependency 'rake', '~> 13.0'
s.add_development_dependency 'minitest-match_json', '~> 0.2.0'
end
38 changes: 36 additions & 2 deletions lib/kramdown-prismic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,40 @@

require 'kramdown'

require 'kramdown/parser/prismic'
require 'kramdown/converter/prismic'
require 'kramdown-prismic/version'
require 'kramdown-prismic/format_util'
require 'kramdown-prismic/parser/import_api'
require 'kramdown-prismic/parser/migration_api'
require 'kramdown-prismic/converter/import_api'
require 'kramdown-prismic/converter/migration_api'

# Injects Prismic converters and parsers so that Kramdown can see them
#
# At a high level we implement only one format for each and convert the
# other into it. Since conversion from the V1 to the V1 API format is
# simpler - we only need to inline a few files into their enclosing
# elements (see `FormatUtil::V2` for details) instead of tracking which
# of the fields should be nested from a flatter represenation - in each
# case we choose the format we can apply the V1 -> V2 conversion.
#
# This means that:
# * for the converter it's more conventient to implement it on the V1
# format and then use `FormatUtil::V2.from_v1` to flatten it
# appropriately _after_ the conversion, if output is in V2,
# * for the parser it's more convenient to implement it on the V2
# format and then use `FormatUtil::V2.from_v1` to flatten it
# appropriately before parsing, if input is in V1.
module Kramdown
module Converter
Prismic = KramdownPrismic::Converter::ImportApi
PrismicV2 = KramdownPrismic::Converter::MigrationApi
end

module Parser
# NOTE: odd constant naming is due to how `input` parameter -> classname conversion is handled for
# parser, which - contrary to converters - doesn't handle PascalCase correctly:
# https://github.com/gettalong/kramdown/blob/REL_2_4_0/lib/kramdown/document.rb#L98-L102
Prismic_v2 = KramdownPrismic::Parser::MigrationApi
Prismic = KramdownPrismic::Parser::ImportApi
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@

require 'kramdown/converter/base'

module Kramdown
module KramdownPrismic
module Converter
class Prismic < Base
Utils = Kramdown::Utils
Element = Kramdown::Element

# Converter for the Prismic API V1 format (aka Import API format)
class ImportApi < Kramdown::Converter::Base
def convert(root)
cleanup_ast(root).map do |child|
convert_element(child)
end.compact.flatten
end

private
protected

def cleanup_ast(root)
remove_blanks(root)
Expand Down Expand Up @@ -56,7 +60,28 @@ def extract_non_nestable_elements(child, elements)
end

def convert_element(element)
send("convert_#{element.type}", element)
result = send("convert_#{element.type}", element)

case result
when nil then nil
when Array
result.map(&method(:with_sorted_spans))
else
with_sorted_spans(result)
end
end

# Sort spans to match default Prismic ordering - it is not
# an API requirement, but makes testing easier if you have
# a matching ordering
def with_sorted_spans(element)
if (spans = element.dig(:content, :spans))
element[:content][:spans] = spans.sort do |lhs, rhs|
[lhs[:type], lhs[:start]] <=> [rhs[:type], rhs[:start]]
end
end

element
end

def convert_header(element)
Expand Down Expand Up @@ -133,6 +158,7 @@ def convert_img(element)
end

# This can only apply when an link with an image inside has been detected
# TODO: This is not quite true, it's also callded with (at least) a top-level `<a>` as well
def convert_a(element)
image = element.children.first
{
Expand Down Expand Up @@ -173,11 +199,11 @@ def convert_sub_html_element(element, type)
def convert_html_element(element)
if element.value == 'iframe'
{
type: 'embed',
content: {
spans: [],
text: ''
text: '',
spans: []
},
type: 'embed',
data: {
embed_url: element.attr['src'],
type: 'link'
Expand Down Expand Up @@ -235,10 +261,21 @@ def extract_content(element, memo = { text: '', spans: [] })
end
end

def insert_span(element, memo, span)
def insert_span(element, memo, span_attributes)
# Attributes are inserted in order of:
# type, start, end, other attributes
# again, for slighly simpler comparisions
# against typical Prismic ordering in tests
span = {
type: span_attributes.delete(:type),
}

span[:start] = memo[:text].size
extract_content(element, memo)
span[:end] = memo[:text].size

span.merge!(span_attributes)

memo[:spans] << span
memo
end
Expand Down
24 changes: 24 additions & 0 deletions lib/kramdown-prismic/converter/migration_api.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

require_relative 'import_api'

module KramdownPrismic
module Converter
# Converter for the Prismic API V2 format (aka Migration API format)
#
# It's basically thing wrapper over the V1 converter, that applies
# common conversion rules between those formats when emitting an element
class MigrationApi < ImportApi
def convert_element(element)
result = super

return nil unless result

case result
when Array then result.map(&FormatUtil::V2.method(:from_v1))
else FormatUtil::V2.from_v1(result)
end
end
end
end
end
Loading