mint 0.7.4 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +23 -14
  3. data/LICENSE +22 -0
  4. data/README.md +68 -79
  5. data/bin/mint +47 -10
  6. data/bin/mint-epub +1 -4
  7. data/config/templates/base/style.css +187 -0
  8. data/config/templates/default/layout.erb +10 -0
  9. data/config/templates/default/style.css +237 -0
  10. data/config/templates/garden/layout.erb +38 -0
  11. data/config/templates/garden/style.css +303 -0
  12. data/config/templates/nord/layout.erb +11 -0
  13. data/config/templates/nord/style.css +339 -0
  14. data/config/templates/nord-dark/layout.erb +11 -0
  15. data/config/templates/nord-dark/style.css +339 -0
  16. data/config/templates/zen/layout.erb +11 -0
  17. data/config/templates/zen/style.css +114 -0
  18. data/lib/mint/command_line.rb +253 -111
  19. data/lib/mint/css.rb +11 -4
  20. data/lib/mint/css_parser.rb +76 -0
  21. data/lib/mint/css_template.rb +37 -0
  22. data/lib/mint/document.rb +203 -43
  23. data/lib/mint/helpers.rb +50 -10
  24. data/lib/mint/layout.rb +2 -3
  25. data/lib/mint/markdown_template.rb +47 -0
  26. data/lib/mint/mint.rb +181 -114
  27. data/lib/mint/plugin.rb +3 -3
  28. data/lib/mint/plugins/epub.rb +1 -2
  29. data/lib/mint/resource.rb +19 -9
  30. data/lib/mint/style.rb +10 -14
  31. data/lib/mint/version.rb +1 -1
  32. data/lib/mint.rb +1 -0
  33. data/man/mint.1 +135 -0
  34. data/spec/cli/README.md +99 -0
  35. data/spec/cli/argument_parsing_spec.rb +237 -0
  36. data/spec/cli/bin_integration_spec.rb +348 -0
  37. data/spec/cli/configuration_management_spec.rb +363 -0
  38. data/spec/cli/full_workflow_integration_spec.rb +527 -0
  39. data/spec/cli/publish_workflow_spec.rb +368 -0
  40. data/spec/cli/template_management_spec.rb +300 -0
  41. data/spec/css_parser_spec.rb +149 -0
  42. data/spec/css_spec.rb +1 -1
  43. data/spec/document_spec.rb +102 -69
  44. data/spec/helpers_spec.rb +42 -42
  45. data/spec/mint_spec.rb +104 -80
  46. data/spec/plugin_spec.rb +141 -143
  47. data/spec/run_cli_tests.rb +95 -0
  48. data/spec/spec_helper.rb +8 -1
  49. data/spec/style_spec.rb +18 -16
  50. data/spec/support/cli_helpers.rb +169 -0
  51. data/spec/support/fixtures/content-2.md +16 -0
  52. data/spec/support/matchers.rb +1 -1
  53. metadata +116 -224
  54. data/config/syntax.yaml +0 -71
  55. data/config/templates/base/style.sass +0 -144
  56. data/config/templates/default/css/style.css +0 -158
  57. data/config/templates/default/layout.haml +0 -8
  58. data/config/templates/default/style.sass +0 -36
  59. data/config/templates/protocol/layout.haml +0 -7
  60. data/config/templates/protocol/style.sass +0 -20
  61. data/config/templates/zen/css/style.css +0 -145
  62. data/config/templates/zen/layout.haml +0 -7
  63. data/config/templates/zen/style.sass +0 -24
  64. data/features/config.feature +0 -21
  65. data/features/plugins/epub.feature +0 -23
  66. data/features/publish.feature +0 -73
  67. data/features/support/env.rb +0 -15
  68. data/features/templates.feature +0 -79
  69. data/spec/command_line_spec.rb +0 -87
  70. data/spec/plugins/epub_spec.rb +0 -242
@@ -0,0 +1,149 @@
1
+ require "spec_helper"
2
+ require "mint/css_parser"
3
+ require "tempfile"
4
+ require "pathname"
5
+
6
+ describe Mint::CssParser do
7
+ describe ".extract_imports" do
8
+ it "extracts @import statements with double quotes" do
9
+ css = '@import "reset.css"; body { margin: 0; }'
10
+ imports = Mint::CssParser.extract_imports(css)
11
+ expect(imports).to eq(["reset.css"])
12
+ end
13
+
14
+ it "extracts @import statements with single quotes" do
15
+ css = "@import 'normalize.css'; .class { color: red; }"
16
+ imports = Mint::CssParser.extract_imports(css)
17
+ expect(imports).to eq(["normalize.css"])
18
+ end
19
+
20
+ it "extracts @import url() statements" do
21
+ css = '@import url("fonts.css"); @import url(\'colors.css\');'
22
+ imports = Mint::CssParser.extract_imports(css)
23
+ expect(imports).to eq(["fonts.css", "colors.css"])
24
+ end
25
+
26
+ it "extracts multiple @import statements" do
27
+ css = <<~CSS
28
+ @import "reset.css";
29
+ @import 'normalize.css';
30
+ @import url("fonts.css");
31
+ body { margin: 0; }
32
+ CSS
33
+ imports = Mint::CssParser.extract_imports(css)
34
+ expect(imports).to eq(["reset.css", "normalize.css", "fonts.css"])
35
+ end
36
+
37
+ it "returns empty array when no imports found" do
38
+ css = "body { margin: 0; color: blue; }"
39
+ imports = Mint::CssParser.extract_imports(css)
40
+ expect(imports).to eq([])
41
+ end
42
+ end
43
+
44
+ describe ".resolve_css_files" do
45
+ let(:temp_dir) { Dir.mktmpdir }
46
+
47
+ after do
48
+ FileUtils.rm_rf(temp_dir)
49
+ end
50
+
51
+ it "resolves main CSS file path relative to HTML output" do
52
+ # Create directory structure:
53
+ # temp_dir/
54
+ # css/
55
+ # main.css
56
+ # output/
57
+ # index.html (output file)
58
+
59
+ css_dir = File.join(temp_dir, "css")
60
+ output_dir = File.join(temp_dir, "output")
61
+ FileUtils.mkdir_p([css_dir, output_dir])
62
+
63
+ main_css = File.join(css_dir, "main.css")
64
+ html_output = File.join(output_dir, "index.html")
65
+
66
+ File.write(main_css, "body { margin: 0; }")
67
+
68
+ css_files = Mint::CssParser.resolve_css_files(main_css, html_output)
69
+ expect(css_files).to eq(["../css/main.css"])
70
+ end
71
+
72
+ it "resolves main CSS and imported files" do
73
+ # Create directory structure:
74
+ # temp_dir/
75
+ # css/
76
+ # main.css (imports reset.css)
77
+ # reset.css
78
+ # output/
79
+ # index.html
80
+
81
+ css_dir = File.join(temp_dir, "css")
82
+ output_dir = File.join(temp_dir, "output")
83
+ FileUtils.mkdir_p([css_dir, output_dir])
84
+
85
+ main_css = File.join(css_dir, "main.css")
86
+ reset_css = File.join(css_dir, "reset.css")
87
+ html_output = File.join(output_dir, "index.html")
88
+
89
+ File.write(main_css, '@import "reset.css"; body { margin: 0; }')
90
+ File.write(reset_css, "* { box-sizing: border-box; }")
91
+
92
+ css_files = Mint::CssParser.resolve_css_files(main_css, html_output)
93
+ expect(css_files).to eq(["../css/main.css", "../css/reset.css"])
94
+ end
95
+
96
+ it "ignores non-existent imported files" do
97
+ css_dir = File.join(temp_dir, "css")
98
+ output_dir = File.join(temp_dir, "output")
99
+ FileUtils.mkdir_p([css_dir, output_dir])
100
+
101
+ main_css = File.join(css_dir, "main.css")
102
+ html_output = File.join(output_dir, "index.html")
103
+
104
+ File.write(main_css, '@import "nonexistent.css"; body { margin: 0; }')
105
+
106
+ css_files = Mint::CssParser.resolve_css_files(main_css, html_output)
107
+ expect(css_files).to eq(["../css/main.css"])
108
+ end
109
+
110
+ it "only processes .css files" do
111
+ scss_dir = File.join(temp_dir, "scss")
112
+ output_dir = File.join(temp_dir, "output")
113
+ FileUtils.mkdir_p([scss_dir, output_dir])
114
+
115
+ main_scss = File.join(scss_dir, "main.scss")
116
+ html_output = File.join(output_dir, "index.html")
117
+
118
+ File.write(main_scss, '@import "reset"; body { margin: 0; }')
119
+
120
+ css_files = Mint::CssParser.resolve_css_files(main_scss, html_output)
121
+ expect(css_files).to eq(["../scss/main.scss"])
122
+ end
123
+ end
124
+
125
+ describe ".generate_link_tags" do
126
+ it "generates HTML link tags for CSS files" do
127
+ css_paths = ["../css/main.css", "../css/reset.css"]
128
+
129
+ html = Mint::CssParser.generate_link_tags(css_paths)
130
+
131
+ expected = <<~HTML.strip
132
+ <link rel="stylesheet" href="../css/main.css">
133
+ <link rel="stylesheet" href="../css/reset.css">
134
+ HTML
135
+
136
+ expect(html).to eq(expected)
137
+ end
138
+
139
+ it "handles empty array" do
140
+ html = Mint::CssParser.generate_link_tags([])
141
+ expect(html).to eq("")
142
+ end
143
+
144
+ it "handles single CSS file" do
145
+ html = Mint::CssParser.generate_link_tags(["styles.css"])
146
+ expect(html).to eq('<link rel="stylesheet" href="styles.css">')
147
+ end
148
+ end
149
+ end
data/spec/css_spec.rb CHANGED
@@ -39,7 +39,7 @@ module Mint
39
39
 
40
40
  describe ".parse" do
41
41
  it "transforms a map of human-readable styles into a CSS string" do
42
- CSS.parse({ "Font" => "Helvetica" }).should == "#container {\n font-family: Helvetica; }\n"
42
+ expect(CSS.parse({ "Font" => "Helvetica" })).to eq("#container {\n font-family: Helvetica;\n}")
43
43
  end
44
44
  end
45
45
  end
@@ -14,20 +14,27 @@ module Mint
14
14
  #
15
15
  # This test doesn't cover any plugin transformations. Those
16
16
  # transformations are covered in the Plugin spec.
17
- its(:content) { should =~ /<p>This is just a test.<\/p>/ }
18
- its(:metadata) { should == { "metadata" => true } }
17
+ its(:content) { is_expected.to match(/<p>This is just a test.<\/p>/) }
18
+ its(:metadata) { is_expected.to eq({ "metadata" => true }) }
19
19
 
20
20
  # Render output
21
21
 
22
22
  # This test doesn't cover any plugin transformations. Those
23
23
  # transformations are covered in the Plugin spec.
24
24
  it "renders its layout, injecting content inside" do
25
- document.render.should =~
26
- /.*<html>.*#{document.content}.*<\/html>.*/m
25
+ expect(document.render).to include(document.content)
26
+ expect(document.render).to include("<html")
27
+ expect(document.render).to include("</html>")
27
28
  end
28
29
 
29
- it "links to its stylesheet" do
30
- document.render.should =~ /#{document.stylesheet}/
30
+ it "includes its stylesheet appropriately based on style mode" do
31
+ if document.style_mode == :external
32
+ expect(document.render).to include('<link rel="stylesheet"')
33
+ expect(document.render).not_to include("<style>")
34
+ else
35
+ expect(document.render).to include("<style>")
36
+ expect(document.render).to include("</style>")
37
+ end
31
38
  end
32
39
 
33
40
  # Mint output
@@ -36,14 +43,19 @@ module Mint
36
43
  # transformations are covered in the Plugin spec.
37
44
  it "writes its rendered style to #style_destination_file" do
38
45
  document.publish!
39
- document.style_destination_file_path.should exist
46
+ if document.style_mode == :external
47
+ expect(document.style_destination_file_path).to exist
48
+ else
49
+ # For inline styles, no external file should be created
50
+ expect(document.style_destination_file_path).not_to exist
51
+ end
40
52
  end
41
53
 
42
54
  it "writes its rendered layout and content to #destination_file" do
43
55
  document.publish!
44
- document.destination_file_path.should exist
56
+ expect(document.destination_file_path).to exist
45
57
  content = File.read document.destination_file
46
- content.should == document.render
58
+ expect(content).to eq(document.render)
47
59
  end
48
60
  end
49
61
 
@@ -51,100 +63,119 @@ module Mint
51
63
  let(:document) { Document.new @content_file }
52
64
 
53
65
  subject { document }
54
- its(:root) { should == @tmp_dir }
55
- its(:destination) { should be_nil }
56
- its(:source) { should == "content.md" }
57
- its(:style_destination) { should be_nil }
66
+ its(:root) { is_expected.to eq(@tmp_dir) }
67
+ its(:destination) { is_expected.to be_nil }
68
+ its(:source) { is_expected.to eq("content.md") }
69
+ its(:style_destination) { is_expected.to be_nil }
58
70
 
59
- its(:style_destination_file) do
60
- should == Mint.root + "/config/templates/default/css/style.css"
71
+ it "has a style destination file in user tmp directory" do
72
+ expect(document.style_destination_file).to match(/\.config\/mint\/tmp\/style\.css$/)
61
73
  end
62
74
 
63
- its(:style_destination_directory) do
64
- should == Mint.root + "/config/templates/default/css"
75
+ it "has a style destination directory in user tmp directory" do
76
+ expect(document.style_destination_directory).to match(/\.config\/mint\/tmp$/)
65
77
  end
66
78
 
67
79
  its(:style_destination_file_path) do
68
- should == Pathname.new(document.style_destination_file)
80
+ is_expected.to eq(Pathname.new(document.style_destination_file))
69
81
  end
70
82
 
71
83
  its(:style_destination_directory_path) do
72
- should == Pathname.new(document.style_destination_directory)
84
+ is_expected.to eq(Pathname.new(document.style_destination_directory))
73
85
  end
74
86
 
75
- its(:layout) { should be_in_directory("default") }
76
- its(:style) { should be_in_directory("default") }
87
+ its(:layout) { is_expected.to be_in_directory("default") }
88
+ its(:style) { is_expected.to be_in_directory("default") }
77
89
 
78
- its(:stylesheet) { should == Mint.root + "/config/templates/default/css/style.css" }
90
+ it "has a stylesheet path relative to user tmp directory" do
91
+ expect(document.stylesheet).to match(/\.config\/mint\/tmp\/style\.css$/)
92
+ end
79
93
 
80
94
  it_should_behave_like "all documents"
81
95
  end
82
96
 
83
97
  context "when it's created with explicit destination directories" do
84
98
  let(:document) { Document.new @content_file,
85
- :destination => "destination",
86
- :style_destination => "styles" }
99
+ destination: "destination",
100
+ style_destination: "styles" }
87
101
 
88
102
  subject { document }
89
- its(:root) { should == @tmp_dir }
90
- its(:destination) { should == "destination" }
91
- its(:source) { should == "content.md" }
92
- its(:style_destination) { should == "styles" }
103
+ its(:root) { is_expected.to eq(@tmp_dir) }
104
+ its(:destination) { is_expected.to eq("destination") }
105
+ its(:source) { is_expected.to eq("content.md") }
106
+ its(:style_destination) { is_expected.to eq("styles") }
93
107
 
94
108
  its(:style_destination_file) do
95
- should == "#{@tmp_dir}/destination/styles/style.css"
109
+ is_expected.to eq("#{@tmp_dir}/destination/styles/style.css")
96
110
  end
97
111
 
98
112
  its(:style_destination_directory) do
99
- should == "#{@tmp_dir}/destination/styles"
113
+ is_expected.to eq("#{@tmp_dir}/destination/styles")
100
114
  end
101
115
 
102
116
  its(:style_destination_file_path) do
103
- should == Pathname.new(document.style_destination_file)
117
+ is_expected.to eq(Pathname.new(document.style_destination_file))
104
118
  end
105
119
 
106
120
  its(:style_destination_directory_path) do
107
- should == Pathname.new(document.style_destination_directory)
121
+ is_expected.to eq(Pathname.new(document.style_destination_directory))
108
122
  end
109
123
 
110
- its(:layout) { should be_in_directory("default") }
111
- its(:style) { should be_in_directory("default") }
124
+ its(:layout) { is_expected.to be_in_directory("default") }
125
+ its(:style) { is_expected.to be_in_directory("default") }
112
126
 
113
- its(:stylesheet) { should == "styles/style.css" }
127
+ it "has a stylesheet path relative to user tmp directory" do
128
+ expect(document.stylesheet).to match(/\.config\/mint\/tmp\/style\.css$/)
129
+ end
114
130
 
115
131
  it_should_behave_like "all documents"
116
132
  end
117
133
 
118
134
  context "when it's created with an explicit root" do
119
135
  let(:document) { Document.new @content_file,
120
- :root => "#{@tmp_dir}/alternative-root" }
136
+ root: "#{@tmp_dir}/alternative-root" }
121
137
 
122
138
  subject { document }
123
- its(:root) { should == "#{@tmp_dir}/alternative-root" }
124
- its(:destination) { should be_nil }
125
- its(:source) { should == "content.md" }
126
- its(:style_destination) { should be_nil }
127
-
128
- its(:style_destination_file) do
129
- should == Mint.root + "/config/templates/default/css/style.css"
139
+ its(:root) { is_expected.to eq("#{@tmp_dir}/alternative-root") }
140
+ it "preserves folder structure" do
141
+ expect(document.destination).to be_present
142
+ end
143
+ its(:source) { is_expected.to eq("content.md") }
144
+ its(:style_destination) { is_expected.to be_nil }
145
+
146
+ it "has appropriate style behavior based on style mode" do
147
+ if document.style_mode == :external
148
+ expect(document.style_destination_file).to match(/\.config\/mint\/tmp\/style\.css$/)
149
+ else
150
+ # For inline styles, the style_destination_file should still exist as a path
151
+ # but no actual file should be created during publish
152
+ expect(document.style_destination_file).to be_present
153
+ end
130
154
  end
131
155
 
132
- its(:style_destination_directory) do
133
- should == Mint.root + "/config/templates/default/css"
156
+ it "has appropriate style destination directory based on style mode" do
157
+ if document.style_mode == :external
158
+ expect(document.style_destination_directory).to match(/\.config\/mint\/tmp$/)
159
+ else
160
+ # For inline styles, still has a directory path but it's not used for external files
161
+ expect(document.style_destination_directory).to be_present
162
+ end
134
163
  end
135
164
 
136
165
  its(:style_destination_file_path) do
137
- should == Pathname.new(document.style_destination_file)
166
+ is_expected.to eq(Pathname.new(document.style_destination_file))
138
167
  end
139
168
 
140
169
  its(:style_destination_directory_path) do
141
- should == Pathname.new(document.style_destination_directory)
170
+ is_expected.to eq(Pathname.new(document.style_destination_directory))
142
171
  end
143
172
 
144
- its(:layout) { should be_in_directory("default") }
145
- its(:style) { should be_in_directory("default") }
173
+ its(:layout) { is_expected.to be_in_directory("default") }
174
+ its(:style) { is_expected.to be_in_directory("default") }
146
175
 
147
- its(:stylesheet) { should == Mint.root + "/config/templates/default/css/style.css" }
176
+ it "has a stylesheet path relative to user tmp directory" do
177
+ expect(document.stylesheet).to match(/\.config\/mint\/tmp\/style\.css$/)
178
+ end
148
179
 
149
180
  it_should_behave_like "all documents"
150
181
  end
@@ -161,31 +192,33 @@ module Mint
161
192
  end
162
193
 
163
194
  subject { document }
164
- its(:root) { should == "#{@tmp_dir}/alternative-root" }
165
- its(:destination) { should == "destination" }
166
- its(:source) { should == "content.md" }
167
- its(:style_destination) { should == "styles" }
195
+ its(:root) { is_expected.to eq("#{@tmp_dir}/alternative-root") }
196
+ its(:destination) { is_expected.to eq("destination") }
197
+ its(:source) { is_expected.to eq("content.md") }
198
+ its(:style_destination) { is_expected.to eq("styles") }
168
199
 
169
200
  its(:style_destination_file) do
170
- should == "#{@tmp_dir}/alternative-root/destination/styles/style.css"
201
+ is_expected.to eq("#{@tmp_dir}/alternative-root/destination/styles/style.css")
171
202
  end
172
203
 
173
204
  its(:style_destination_directory) do
174
- should == "#{@tmp_dir}/alternative-root/destination/styles"
205
+ is_expected.to eq("#{@tmp_dir}/alternative-root/destination/styles")
175
206
  end
176
207
 
177
208
  its(:style_destination_file_path) do
178
- should == Pathname.new(document.style_destination_file)
209
+ is_expected.to eq(Pathname.new(document.style_destination_file))
179
210
  end
180
211
 
181
212
  its(:style_destination_directory_path) do
182
- should == Pathname.new(document.style_destination_directory)
213
+ is_expected.to eq(Pathname.new(document.style_destination_directory))
183
214
  end
184
215
 
185
- its(:layout) { should be_in_directory("zen") }
186
- its(:style) { should be_in_directory("zen") }
216
+ its(:layout) { is_expected.to be_in_directory("zen") }
217
+ its(:style) { is_expected.to be_in_directory("zen") }
187
218
 
188
- its(:stylesheet) { should == "styles/style.css" }
219
+ it "has a stylesheet path relative to user tmp directory" do
220
+ expect(document.stylesheet).to match(/\.config\/mint\/tmp\/style\.css$/)
221
+ end
189
222
 
190
223
  it_should_behave_like "all documents"
191
224
  end
@@ -194,33 +227,33 @@ module Mint
194
227
  let(:text) { "metadata: true\n\nReal text" }
195
228
  describe ".metadata_chunk" do
196
229
  it "extracts, but does not parse, metadata from text" do
197
- Document.metadata_chunk(text).should == "metadata: true"
230
+ expect(Document.metadata_chunk(text)).to eq("metadata: true")
198
231
  end
199
232
  end
200
233
 
201
234
  describe ".metadata_from" do
202
235
  it "parses a documents metadata if present" do
203
- Document.metadata_from(text).should == { "metadata" => true }
236
+ expect(Document.metadata_from(text)).to eq({ "metadata" => true })
204
237
  end
205
238
 
206
239
  it "returns the empty string if a document has bad/no metadata" do
207
- Document.metadata_from("No metadata here").should == {}
240
+ expect(Document.metadata_from("No metadata here")).to eq({})
208
241
  end
209
242
 
210
243
  it "handles a non-simple string that is also not YAML" do
211
- Document.metadata_from("# Non-simple string").should == {}
244
+ expect(Document.metadata_from("# Non-simple string")).to eq({})
212
245
  end
213
246
  end
214
247
 
215
248
  describe ".parse_metadata_from" do
216
249
  it "separates text from its metadata if present" do
217
- Document.parse_metadata_from(text).should ==
218
- [{ "metadata" => true }, "Real text"]
250
+ expect(Document.parse_metadata_from(text)).to eq(
251
+ [{ "metadata" => true }, "Real text"])
219
252
  end
220
253
 
221
254
  it "returns the entire text if no metadata is found" do
222
- Document.parse_metadata_from("No metadata here").should ==
223
- [{}, "No metadata here"]
255
+ expect(Document.parse_metadata_from("No metadata here")).to eq(
256
+ [{}, "No metadata here"])
224
257
  end
225
258
  end
226
259
  end