bridgetown-core 1.2.0.beta5 → 1.3.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Roda
4
+ module RodaPlugins
5
+ module BridgetownServer
6
+ SiteContext = Struct.new(:registers) # for use by Liquid-esque URL helpers
7
+
8
+ def self.load_dependencies(app) # rubocop:disable Metrics
9
+ unless Bridgetown::Current.preloaded_configuration
10
+ raise "You must supply a preloaded configuration before loading the Bridgetown Roda " \
11
+ "plugin"
12
+ end
13
+
14
+ app.plugin :initializers
15
+ app.plugin :method_override
16
+ app.plugin :all_verbs
17
+ app.plugin :hooks
18
+ app.plugin :common_logger, Bridgetown::Rack::Logger.new($stdout), method: :info
19
+ app.plugin :json
20
+ app.plugin :json_parser
21
+ app.plugin :indifferent_params
22
+ app.plugin :cookies
23
+ app.plugin :streaming
24
+ app.plugin :public, root: Bridgetown::Current.preloaded_configuration.destination
25
+ app.plugin :not_found do
26
+ output_folder = Bridgetown::Current.preloaded_configuration.destination
27
+ File.read(File.join(output_folder, "404.html"))
28
+ rescue Errno::ENOENT
29
+ "404 Not Found"
30
+ end
31
+ app.plugin :exception_page
32
+ app.plugin :error_handler do |e|
33
+ Bridgetown::Errors.print_build_error(
34
+ e, logger: Bridgetown::LogAdapter.new(self.class.opts[:common_logger]), server: true
35
+ )
36
+ next exception_page(e) if ENV.fetch("RACK_ENV", nil) == "development"
37
+
38
+ output_folder = Bridgetown::Current.preloaded_configuration.destination
39
+ File.read(File.join(output_folder, "500.html"))
40
+ rescue Errno::ENOENT
41
+ "500 Internal Server Error"
42
+ end
43
+
44
+ ExceptionPage.class_eval do # rubocop:disable Metrics/BlockLength
45
+ def self.css
46
+ <<~CSS
47
+ html * { padding:0; margin:0; }
48
+ body * { padding:10px 20px; }
49
+ body * * { padding:0; }
50
+ body { font-family: -apple-system, sans-serif; font-size: 90%; }
51
+ body>div { border-bottom:1px solid #ddd; }
52
+ code { font-family: ui-monospace, monospace; }
53
+ h1 { font-weight: bold; margin-block-end: .8em; }
54
+ h2 { margin-block-end:.8em; }
55
+ h2 span { font-size:80%; color:#f7f7db; font-weight:normal; }
56
+ h3 { margin:1em 0 .5em 0; }
57
+ h4 { margin:0 0 .5em 0; font-weight: normal; }
58
+ table {
59
+ border:1px solid #ccc; border-collapse: collapse; background:white; }
60
+ tbody td, tbody th { vertical-align:top; padding:2px 3px; }
61
+ thead th {
62
+ padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
63
+ font-weight:normal; font-size:11px; border:1px solid #ddd; }
64
+ tbody th { text-align:right; opacity: 0.7; padding-right:.5em; }
65
+ table.vars { margin:5px 0 2px 40px; }
66
+ table.vars td, table.req td { font-family: ui-monospace, monospace; }
67
+ table td.code { width:100%;}
68
+ table td.code div { overflow:hidden; }
69
+ table.source th { color:#666; }
70
+ table.source td {
71
+ font-family: ui-monospace, monospace; white-space:pre; border-bottom:1px solid #eee; }
72
+ ul.traceback { list-style-type:none; }
73
+ ul.traceback li.frame { margin-bottom:1em; }
74
+ div.context { margin: 10px 0; }
75
+ div.context ol {
76
+ padding-left:30px; margin:0 10px; list-style-position: inside; }
77
+ div.context ol li {
78
+ font-family: ui-monospace, monospace; white-space:pre; color:#666; cursor:pointer; }
79
+ div.context ol.context-line li { color:black; background-color:#f7f7db; }
80
+ div.context ol.context-line li span { float: right; }
81
+ div.commands { margin-left: 40px; }
82
+ div.commands a { color:black; text-decoration:none; }
83
+ #summary { background: #1D453C; color: white; }
84
+ #summary h2 { font-weight: normal; color: white; }
85
+ #summary ul#quicklinks { list-style-type: none; margin-bottom: 2em; }
86
+ #summary ul#quicklinks li { float: left; padding: 0 1em; }
87
+ #summary ul#quicklinks>li+li { border-left: 1px #666 solid; }
88
+ #summary a { color: #f47c3c; }
89
+ #explanation { background:#eee; }
90
+ #traceback { background: white; }
91
+ #requestinfo { background:#f6f6f6; padding-left:120px; }
92
+ #summary table { border:none; background:transparent; }
93
+ #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
94
+ #requestinfo h3 { margin-bottom:-1em; }
95
+ .error { background: #ffc; }
96
+ .specific { color:#cc3300; font-weight:bold; }
97
+ CSS
98
+ end
99
+ end
100
+
101
+ app.before do
102
+ if self.class.opts[:bridgetown_site]
103
+ # The site had previously been initialized via the bridgetown_ssr plugin
104
+ Bridgetown::Current.sites[self.class.opts[:bridgetown_site].label] =
105
+ self.class.opts[:bridgetown_site]
106
+ @context ||= SiteContext.new({ site: self.class.opts[:bridgetown_site] })
107
+ end
108
+ Bridgetown::Current.preloaded_configuration ||=
109
+ self.class.opts[:bridgetown_preloaded_config]
110
+
111
+ request.root do
112
+ output_folder = Bridgetown::Current.preloaded_configuration.destination
113
+ File.read(File.join(output_folder, "index.html"))
114
+ rescue StandardError
115
+ response.status = 500
116
+ "<p>ERROR: cannot find <code>index.html</code> in the output folder.</p>"
117
+ end
118
+ end
119
+ end
120
+
121
+ Roda::RodaRequest.alias_method :_previous_roda_cookies, :cookies
122
+
123
+ module RequestMethods
124
+ # Monkeypatch Roda/Rack's Request object so it returns a hash which allows for
125
+ # indifferent access
126
+ def cookies
127
+ # TODO: maybe replace with a simpler hash that offers an overloaded `[]` method
128
+ _previous_roda_cookies.with_indifferent_access
129
+ end
130
+
131
+ # Starts up the Bridgetown routing system
132
+ def bridgetown
133
+ Bridgetown::Rack::Routes.start!(scope)
134
+ end
135
+ end
136
+ end
137
+
138
+ register_plugin :bridgetown_server, BridgetownServer
139
+ end
140
+ end
@@ -24,10 +24,13 @@ gem "bridgetown", "~> <%= Bridgetown::VERSION %>"
24
24
  # Uncomment to add file-based dynamic routing to your project:
25
25
  # gem "bridgetown-routes", "~> <%= Bridgetown::VERSION %>"
26
26
 
27
+ # Puma is the Rack-compatible web server used by Bridgetown
28
+ # (you can optionally limit this to the "development" group)
29
+ gem "puma", "< 7"
30
+
27
31
  # Uncomment to use the Inspectors API to manipulate the output
28
32
  # of your HTML or XML resources:
29
33
  # gem "nokogiri", "~> 1.13"
30
34
 
31
- # Puma is a Rack-compatible server used by Bridgetown
32
- # (you can optionally limit this to the "development" group)
33
- gem "puma", "~> 5.6"
35
+ # Or for faster parsing of HTML-only resources via Inspectors, use Nokolexbor:
36
+ # gem "nokolexbor", "~> 0.4"
@@ -2,9 +2,11 @@
2
2
  # on the concept of a routing tree. Bridgetown uses it for its development
3
3
  # server, but you can also run it in production for fast, dynamic applications.
4
4
  #
5
- # Learn more at: http://roda.jeremyevans.net
5
+ # Learn more at: https://www.bridgetownrb.com/docs/routes
6
+
7
+ class RodaApp < Roda
8
+ plugin :bridgetown_server
6
9
 
7
- class RodaApp < Bridgetown::Rack::Roda
8
10
  # Some Roda configuration is handled in the `config/initializers.rb` file.
9
11
  # But you can also add additional Roda configuration here if needed.
10
12
 
@@ -5,7 +5,7 @@ date: <%= Time.now.strftime('%Y-%m-%d %H:%M:%S %z') %>
5
5
  categories: updates
6
6
  ---
7
7
 
8
- You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `bridgetown serve`, which launches a web server and auto-regenerates your site when a file is updated.
8
+ You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `bin/bridgetown start`, which launches a web server and auto-regenerates your site when a file is updated.
9
9
 
10
10
  Bridgetown requires blog post files to be named according to the following format:
11
11
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bridgetown-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0.beta5
4
+ version: 1.3.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bridgetown Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-02 00:00:00.000000000 Z
11
+ date: 2023-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -112,28 +112,28 @@ dependencies:
112
112
  requirements:
113
113
  - - "~>"
114
114
  - !ruby/object:Gem::Version
115
- version: '1.0'
115
+ version: '2.0'
116
116
  type: :runtime
117
117
  prerelease: false
118
118
  version_requirements: !ruby/object:Gem::Requirement
119
119
  requirements:
120
120
  - - "~>"
121
121
  - !ruby/object:Gem::Version
122
- version: '1.0'
122
+ version: '2.0'
123
123
  - !ruby/object:Gem::Dependency
124
- name: faraday_middleware
124
+ name: faraday-follow_redirects
125
125
  requirement: !ruby/object:Gem::Requirement
126
126
  requirements:
127
127
  - - "~>"
128
128
  - !ruby/object:Gem::Version
129
- version: '1.0'
129
+ version: '0.3'
130
130
  type: :runtime
131
131
  prerelease: false
132
132
  version_requirements: !ruby/object:Gem::Requirement
133
133
  requirements:
134
134
  - - "~>"
135
135
  - !ruby/object:Gem::Version
136
- version: '1.0'
136
+ version: '0.3'
137
137
  - !ruby/object:Gem::Dependency
138
138
  name: hash_with_dot_access
139
139
  requirement: !ruby/object:Gem::Requirement
@@ -274,20 +274,6 @@ dependencies:
274
274
  - - "~>"
275
275
  - !ruby/object:Gem::Version
276
276
  version: '1.0'
277
- - !ruby/object:Gem::Dependency
278
- name: terminal-table
279
- requirement: !ruby/object:Gem::Requirement
280
- requirements:
281
- - - "~>"
282
- - !ruby/object:Gem::Version
283
- version: '1.8'
284
- type: :runtime
285
- prerelease: false
286
- version_requirements: !ruby/object:Gem::Requirement
287
- requirements:
288
- - - "~>"
289
- - !ruby/object:Gem::Version
290
- version: '1.8'
291
277
  - !ruby/object:Gem::Dependency
292
278
  name: thor
293
279
  requirement: !ruby/object:Gem::Requirement
@@ -316,20 +302,6 @@ dependencies:
316
302
  - - "~>"
317
303
  - !ruby/object:Gem::Version
318
304
  version: '2.0'
319
- - !ruby/object:Gem::Dependency
320
- name: webrick
321
- requirement: !ruby/object:Gem::Requirement
322
- requirements:
323
- - - "~>"
324
- - !ruby/object:Gem::Version
325
- version: '1.7'
326
- type: :runtime
327
- prerelease: false
328
- version_requirements: !ruby/object:Gem::Requirement
329
- requirements:
330
- - - "~>"
331
- - !ruby/object:Gem::Version
332
- version: '1.7'
333
305
  - !ruby/object:Gem::Dependency
334
306
  name: zeitwerk
335
307
  requirement: !ruby/object:Gem::Requirement
@@ -382,8 +354,6 @@ files:
382
354
  - lib/bridgetown-core/commands/new.rb
383
355
  - lib/bridgetown-core/commands/plugins.rb
384
356
  - lib/bridgetown-core/commands/registrations.rb
385
- - lib/bridgetown-core/commands/serve.rb
386
- - lib/bridgetown-core/commands/serve/servlet.rb
387
357
  - lib/bridgetown-core/commands/start.rb
388
358
  - lib/bridgetown-core/commands/webpack.rb
389
359
  - lib/bridgetown-core/commands/webpack/enable-postcss.rb
@@ -501,7 +471,6 @@ files:
501
471
  - lib/bridgetown-core/plugin_manager.rb
502
472
  - lib/bridgetown-core/rack/boot.rb
503
473
  - lib/bridgetown-core/rack/logger.rb
504
- - lib/bridgetown-core/rack/roda.rb
505
474
  - lib/bridgetown-core/rack/routes.rb
506
475
  - lib/bridgetown-core/rack/static_indexes.rb
507
476
  - lib/bridgetown-core/reader.rb
@@ -543,7 +512,7 @@ files:
543
512
  - lib/bridgetown-core/version.rb
544
513
  - lib/bridgetown-core/watcher.rb
545
514
  - lib/bridgetown-core/yaml_parser.rb
546
- - lib/roda/plugins/bridgetown_boot.rb
515
+ - lib/roda/plugins/bridgetown_server.rb
547
516
  - lib/roda/plugins/bridgetown_ssr.rb
548
517
  - lib/roda/plugins/initializers.rb
549
518
  - lib/roda/plugins/method_override.rb
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "webrick"
4
-
5
- module Bridgetown
6
- module Commands
7
- class Serve
8
- class Servlet < WEBrick::HTTPServlet::FileHandler
9
- DEFAULTS = {
10
- "Cache-Control" => "private, max-age=0, proxy-revalidate, " \
11
- "no-store, no-cache, must-revalidate",
12
- }.freeze
13
-
14
- def initialize(server, root, callbacks)
15
- # So we can access them easily.
16
- @bridgetown_opts = server.config[:BridgetownOptions]
17
- set_defaults
18
- super
19
- end
20
-
21
- def search_index_file(req, res)
22
- super ||
23
- search_file(req, res, ".html") ||
24
- search_file(req, res, ".xhtml")
25
- end
26
-
27
- # Add the ability to tap file.html the same way that Nginx does on our
28
- # Docker images (or on GitHub Pages.) The difference is that we might end
29
- # up with a different preference on which comes first.
30
-
31
- def search_file(req, res, basename)
32
- # /file.* > /file/index.html > /file.html
33
- super ||
34
- super(req, res, "#{basename}.html") ||
35
- super(req, res, "#{basename}.xhtml")
36
- end
37
-
38
- # rubocop:disable Naming/MethodName
39
- def do_GET(req, res)
40
- rtn = super
41
-
42
- validate_and_ensure_charset(req, res)
43
- res.header.merge!(@headers)
44
- rtn
45
- end
46
- # rubocop:enable Naming/MethodName
47
-
48
- private
49
-
50
- def validate_and_ensure_charset(_req, res)
51
- key = res.header.keys.grep(%r!content-type!i).first
52
- typ = res.header[key]
53
-
54
- return if %r!;\s*charset=!.match?(typ)
55
-
56
- res.header[key] = "#{typ}; charset=#{@bridgetown_opts["encoding"]}"
57
- end
58
-
59
- def set_defaults
60
- hash_ = @bridgetown_opts.fetch("webrick", {}).fetch("headers", {})
61
- DEFAULTS.each_with_object(@headers = hash_) do |(key, val), hash|
62
- hash[key] = val unless hash.key?(key)
63
- end
64
- end
65
- end
66
- end
67
- end
68
- end
@@ -1,253 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Bridgetown
4
- module Commands
5
- class Serve < Thor::Group
6
- extend BuildOptions
7
- extend Summarizable
8
- include ConfigurationOverridable
9
-
10
- Registrations.register do
11
- register(Serve, "serve", "serve", Serve.summary)
12
- end
13
-
14
- class_option :host, aliases: "-H", desc: "Host to bind to"
15
- class_option :port, aliases: "-P", desc: "Port to listen on"
16
- class_option :detach,
17
- aliases: "-B",
18
- type: :boolean,
19
- desc: "Run the server in the background"
20
- class_option :ssl_cert, desc: "X.509 (SSL) certificate."
21
- class_option :ssl_key, desc: "X.509 (SSL) Private Key."
22
- class_option :show_dir_listing,
23
- type: :boolean,
24
- desc: "Show a directory listing instead of loading your index file."
25
- class_option :skip_initial_build,
26
- type: :boolean,
27
- desc: "Skips the initial site build which occurs before the server is started."
28
- class_option :watch,
29
- type: :boolean,
30
- aliases: "-w",
31
- default: true,
32
- desc: "Watch for changes and rebuild"
33
-
34
- def self.banner
35
- "bridgetown serve [options]"
36
- end
37
- summary "DEPRECATED (Serve your site locally using WEBrick)"
38
-
39
- DIRECTORY_INDEX = %w(
40
- index.htm
41
- index.html
42
- index.rhtml
43
- index.xht
44
- index.xhtml
45
- index.cgi
46
- index.xml
47
- index.json
48
- ).freeze
49
-
50
- def serve
51
- Bridgetown::Deprecator.deprecation_message(
52
- "WEBrick (serve) will be removed in favor of Puma (start) in the next Bridgetown release"
53
- )
54
-
55
- @mutex = Mutex.new
56
- @run_cond = ConditionVariable.new
57
- @running = false
58
-
59
- no_watch = options["watch"] == false
60
-
61
- options = Thor::CoreExt::HashWithIndifferentAccess.new(self.options)
62
- options["serving"] = true
63
- options["watch"] = true unless no_watch
64
-
65
- config = configuration_with_overrides(options, Bridgetown::Current.preloaded_configuration)
66
- if Bridgetown.environment == "development"
67
- default_url(config).tap do |url|
68
- options["url"] = url
69
- config.url = url
70
- end
71
- end
72
-
73
- invoke(Build, [], options)
74
- start_server
75
- end
76
-
77
- protected
78
-
79
- def start_server
80
- destination = Bridgetown::Current.preloaded_configuration.destination
81
- setup(destination)
82
-
83
- start_up_webrick(destination)
84
- end
85
-
86
- def setup(destination)
87
- require_relative "serve/servlet"
88
-
89
- FileUtils.mkdir_p(destination)
90
- return unless File.exist?(File.join(destination, "404.html"))
91
-
92
- WEBrick::HTTPResponse.class_eval do
93
- def create_error_page
94
- @header["Content-Type"] = "text/html; charset=UTF-8"
95
- @body = File.read(File.join(@config[:DocumentRoot], "404.html"))
96
- end
97
- end
98
- end
99
-
100
- def webrick_opts(opts)
101
- opts = {
102
- BridgetownOptions: opts,
103
- DoNotReverseLookup: true,
104
- MimeTypes: mime_types,
105
- DocumentRoot: opts["destination"],
106
- StartCallback: start_callback(opts["detach"]),
107
- StopCallback: stop_callback(opts["detach"]),
108
- BindAddress: opts["host"],
109
- Port: opts["port"],
110
- DirectoryIndex: DIRECTORY_INDEX,
111
- }
112
-
113
- opts[:DirectoryIndex] = [] if opts[:BridgetownOptions]["show_dir_listing"]
114
-
115
- enable_ssl(opts)
116
- enable_logging(opts)
117
- opts
118
- end
119
-
120
- def start_up_webrick(destination)
121
- opts = Bridgetown::Current.preloaded_configuration
122
- @server = WEBrick::HTTPServer.new(webrick_opts(opts)).tap { |o| o.unmount("") }
123
- @server.mount(opts["base_path"].to_s, Servlet, destination, file_handler_opts)
124
-
125
- Bridgetown.logger.info "Server address:", server_address(@server, opts)
126
- launch_browser @server, opts if opts["open_url"]
127
- boot_or_detach @server, opts
128
- end
129
-
130
- def shutdown
131
- @server.shutdown if running?
132
- end
133
-
134
- def default_url(config)
135
- format_url(
136
- config["ssl_cert"] && config["ssl_key"],
137
- config["host"] == "127.0.0.1" ? "localhost" : config["host"],
138
- config["port"]
139
- )
140
- end
141
-
142
- def format_url(ssl_enabled, address, port, baseurl = nil)
143
- format("%<prefix>s://%<address>s:%<port>i%<baseurl>s",
144
- prefix: ssl_enabled ? "https" : "http",
145
- address: address,
146
- port: port,
147
- baseurl: baseurl ? "#{baseurl}/" : "")
148
- end
149
-
150
- # Recreate NondisclosureName under utf-8 circumstance
151
- def file_handler_opts
152
- WEBrick::Config::FileHandler.merge(
153
- FancyIndexing: true,
154
- NondisclosureName: [
155
- ".ht*", "~*",
156
- ]
157
- )
158
- end
159
-
160
- def server_address(server, options = {})
161
- format_url(
162
- server.config[:SSLEnable],
163
- server.config[:BindAddress],
164
- server.config[:Port],
165
- options["baseurl"]
166
- )
167
- end
168
-
169
- # Keep in our area with a thread or detach the server as requested
170
- # by the user. This method determines what we do based on what you
171
- # ask us to do.
172
- def boot_or_detach(server, opts)
173
- if opts["detach"]
174
- pid = Process.fork do
175
- server.start
176
- end
177
-
178
- Process.detach(pid)
179
- Bridgetown.logger.info "Server detached with pid '#{pid}'.", \
180
- "Run `pkill -f bridgetown' or `kill -9 #{pid}' " \
181
- "to stop the server."
182
- else
183
- t = Thread.new { server.start }
184
- trap("INT") { server.shutdown }
185
- t.join
186
- end
187
- end
188
-
189
- # Make the stack verbose if the user requests it.
190
- def enable_logging(opts)
191
- opts[:AccessLog] = []
192
- level = WEBrick::Log.const_get(opts[:BridgetownOptions]["verbose"] ? :DEBUG : :WARN)
193
- opts[:Logger] = WEBrick::Log.new($stdout, level)
194
- end
195
-
196
- # Add SSL to the stack if the user triggers --enable-ssl and they
197
- # provide both types of certificates commonly needed. Raise if they
198
- # forget to add one of the certificates.
199
- def enable_ssl(opts)
200
- cert, key, src =
201
- opts[:BridgetownOptions].values_at("ssl_cert", "ssl_key", "source")
202
-
203
- return if cert.nil? && key.nil?
204
- raise "Missing --ssl_cert or --ssl_key. Both are required." unless cert && key
205
-
206
- require "openssl"
207
- require "webrick/https"
208
-
209
- opts[:SSLCertificate] = OpenSSL::X509::Certificate.new(read_file(src, cert))
210
- begin
211
- opts[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(read_file(src, key))
212
- rescue StandardError
213
- raise unless defined?(OpenSSL::PKey::EC)
214
-
215
- opts[:SSLPrivateKey] = OpenSSL::PKey::EC.new(read_file(src, key))
216
- end
217
- opts[:SSLEnable] = true
218
- end
219
-
220
- def start_callback(detached)
221
- return if detached
222
-
223
- proc do
224
- @mutex.synchronize do
225
- @running = true
226
- Bridgetown.logger.info("Server running…", "press ctrl-c to stop.")
227
- @run_cond.broadcast
228
- end
229
- end
230
- end
231
-
232
- def stop_callback(detached)
233
- return if detached
234
-
235
- proc do
236
- @mutex.synchronize do
237
- @running = false
238
- @run_cond.broadcast
239
- end
240
- end
241
- end
242
-
243
- def mime_types
244
- file = File.expand_path("../mime.types", __dir__)
245
- WEBrick::HTTPUtils.load_mime_types(file)
246
- end
247
-
248
- def read_file(source_dir, file_path)
249
- File.read(Bridgetown.sanitized_path(source_dir, file_path))
250
- end
251
- end
252
- end
253
- end