k_builder-webpack5 0.0.2 → 0.0.3
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.
- checksums.yaml +4 -4
- data/.templates/sample.txt +2 -0
- data/.templates/web-project/.gitignore +16 -0
- data/.templates/webpack.config.js.txt +40 -0
- data/k_builder-webpack5.gemspec +1 -0
- data/lib/k_builder/webpack5.rb +6 -0
- data/lib/k_builder/webpack5/json_data.rb +17 -0
- data/lib/k_builder/webpack5/version.rb +1 -1
- data/lib/k_builder/webpack5/webpack_builder.rb +196 -0
- data/lib/k_builder/webpack5/webpack_json_factory.rb +195 -0
- metadata +21 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 485fc9474b23acbed4ca9ad076605b6f840d3a506e2d245a319e2d20f000b4e5
|
4
|
+
data.tar.gz: 4f6b1946cbb2edf5b18ea56f2f25c28de090d833c47e2ddaaa0be33c24080500
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2b7dfede2543af409fe5291361604672d7b4dae981225dec4d6f421c2200a4b2274c5b4a051342e39e6fe4ae1228dcb4923820110a556842006b1c0b58e45458
|
7
|
+
data.tar.gz: ef02d378bdbeb933daba71d37dd511783e651726c79aa91f3cf4eef59a93593e81008550d76b1b6ed680ed70039105097d7e72c438fa52f596084aa0209cad17
|
@@ -0,0 +1,40 @@
|
|
1
|
+
{{> requirePath}}
|
2
|
+
{{> requireWebpack}}
|
3
|
+
{{> requireMiniCssExtractPlugin}}
|
4
|
+
|
5
|
+
{{#if (or mode entry entries dev_server plugins)~}}
|
6
|
+
module.exports = {
|
7
|
+
{{#if mode}}{{as_javascript mode 'exclude_root'}},{{/if~}}
|
8
|
+
{{#if entry}}{{as_javascript entry 'exclude_root'}},{{/if~}}
|
9
|
+
{{#if entries}}entry: {{as_javascript entries}},{{/if~}}
|
10
|
+
{{#if dev_server}}devServer: {{as_javascript dev_server}},{{/if~}}
|
11
|
+
{{#if (or plugins.mini_css_extract)~}}
|
12
|
+
plugins: [
|
13
|
+
{{#if plugins.mini_css_extract}}new MiniCssExtractPlugin({ filename:'{{plugins.mini_css_extract.filename}}' }),{{/if}}
|
14
|
+
],
|
15
|
+
{{/if}}
|
16
|
+
}
|
17
|
+
{{/if}}
|
18
|
+
|
19
|
+
{{#*inline "requirePath"}}
|
20
|
+
{{#if root_scope.require_path}}
|
21
|
+
const path = require("path");
|
22
|
+
{{/if}}
|
23
|
+
{{/inline}}
|
24
|
+
{{#*inline "requireWebpack"}}
|
25
|
+
{{#if root_scope.require_webpack}}
|
26
|
+
const webpack = require('webpack');
|
27
|
+
{{/if}}
|
28
|
+
{{/inline}}
|
29
|
+
{{#*inline "requireMiniCssExtractPlugin"}}
|
30
|
+
{{#if root_scope.require_mini_css_extract_plugin}}
|
31
|
+
/*
|
32
|
+
* MiniCssExtractPlugin enabled. This allows your app to use css modules that will be
|
33
|
+
# moved into a separate CSS file instead of inside one of your module entries!
|
34
|
+
*
|
35
|
+
* https://github.com/webpack-contrib/mini-css-extract-plugin
|
36
|
+
*/
|
37
|
+
|
38
|
+
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
39
|
+
{{/if}}
|
40
|
+
{{/inline}}
|
data/k_builder-webpack5.gemspec
CHANGED
data/lib/k_builder/webpack5.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
4
|
+
require 'k_builder'
|
5
|
+
require 'k_builder/package_json'
|
6
|
+
require 'k_builder/webpack5/json_data'
|
7
|
+
require 'k_builder/webpack5/webpack_builder'
|
8
|
+
require 'k_builder/webpack5/webpack_json_factory'
|
3
9
|
require 'k_builder/webpack5/version'
|
4
10
|
|
5
11
|
module KBuilder
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KBuilder
|
4
|
+
module Webpack5
|
5
|
+
# Represents a node in a JSON object
|
6
|
+
class JsonData < OpenStruct
|
7
|
+
def self.parse_json(json)
|
8
|
+
json = json.to_json if json.is_a?(Hash)
|
9
|
+
JSON.parse(json, object_class: JsonData)
|
10
|
+
end
|
11
|
+
|
12
|
+
def as_json
|
13
|
+
KBuilder.data.struct_to_hash(self)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KBuilder
|
4
|
+
module Webpack5
|
5
|
+
# Configuration currently comes from KBuilder and stores template folders and target folders if configured
|
6
|
+
class WebpackBuilder < KBuilder::Builder
|
7
|
+
# In memory representation of .webpack-rc.json and generator for webpack.config.js
|
8
|
+
# attr_writer :webpack_config
|
9
|
+
attr_writer :webpack_rc
|
10
|
+
|
11
|
+
def initialize(configuration = nil)
|
12
|
+
super(configuration)
|
13
|
+
|
14
|
+
@factory = KBuilder::Webpack5::WebpackJsonFactory
|
15
|
+
end
|
16
|
+
|
17
|
+
# -----------------------------------
|
18
|
+
# Builder Attributes
|
19
|
+
# -----------------------------------
|
20
|
+
|
21
|
+
# def webpack_config
|
22
|
+
# return @webpack_config if defined? @webpack_config
|
23
|
+
|
24
|
+
# load
|
25
|
+
|
26
|
+
# @webpack_config
|
27
|
+
# end
|
28
|
+
|
29
|
+
# def webpack_config_file
|
30
|
+
# # Output Path may not be enough, I may need a webpack output path
|
31
|
+
# @webpack_config_file ||= File.join(target_folder, 'webpack.config.js')
|
32
|
+
# end
|
33
|
+
|
34
|
+
def webpack_rc
|
35
|
+
return @webpack_rc if defined? @webpack_rc
|
36
|
+
|
37
|
+
load_webpack_rc
|
38
|
+
|
39
|
+
@webpack_rc
|
40
|
+
end
|
41
|
+
|
42
|
+
def webpack_rc_file
|
43
|
+
@webpack_rc_file ||= File.join(target_folder, '.webpack-rc.json')
|
44
|
+
end
|
45
|
+
|
46
|
+
# -----------------------------------
|
47
|
+
# Fluent Builder Methods
|
48
|
+
# -----------------------------------
|
49
|
+
|
50
|
+
# Webpack init will create .webconfig-rc.json
|
51
|
+
def webpack_init
|
52
|
+
File.delete(webpack_rc_file) if File.exist?(webpack_rc_file)
|
53
|
+
|
54
|
+
@webpack_rc = @factory.webpack
|
55
|
+
write_webpack_rc
|
56
|
+
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def webpack_dev_server(**dev_server_opts, &block)
|
61
|
+
if @webpack_rc.dev_server.nil?
|
62
|
+
@webpack_rc.dev_server = if block
|
63
|
+
@factory.dev_server(&block)
|
64
|
+
else
|
65
|
+
dev_server_opts = { opinion: @factory.opinion_dev_server } if dev_server_opts.empty?
|
66
|
+
@factory.dev_server(**dev_server_opts)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
write_webpack_rc
|
70
|
+
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
def mode(**mode_opts, &block)
|
75
|
+
if @webpack_rc.mode.nil?
|
76
|
+
@webpack_rc.mode = if block
|
77
|
+
@factory.mode(&block)
|
78
|
+
else
|
79
|
+
mode_opts = { opinion: @factory.opinion_mode } if mode_opts.empty?
|
80
|
+
@factory.mode(**mode_opts)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
write_webpack_rc
|
84
|
+
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
def entry(**entry_opts, &block)
|
89
|
+
if @webpack_rc.entry.nil?
|
90
|
+
@webpack_rc.entry = if block
|
91
|
+
@factory.entry(&block)
|
92
|
+
else
|
93
|
+
entry_opts = { opinion: @factory.opinion_entry } if entry_opts.empty?
|
94
|
+
@factory.entry(**entry_opts)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
write_webpack_rc
|
98
|
+
|
99
|
+
self
|
100
|
+
end
|
101
|
+
|
102
|
+
def entries(&block)
|
103
|
+
if @webpack_rc.entries.nil?
|
104
|
+
@webpack_rc.entries = if block
|
105
|
+
@factory.entries(&block)
|
106
|
+
else
|
107
|
+
entries_opts = { opinion: @factory.opinion_entries }
|
108
|
+
@factory.entries(**entries_opts)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
write_webpack_rc
|
112
|
+
|
113
|
+
self
|
114
|
+
end
|
115
|
+
|
116
|
+
# Plugins
|
117
|
+
|
118
|
+
def plugin_mini_css_extract(**mini_css_extract_opts, &block)
|
119
|
+
ensure_plugins
|
120
|
+
|
121
|
+
if @webpack_rc.plugins.mini_css_extract.nil?
|
122
|
+
@webpack_rc.plugins.mini_css_extract = if block
|
123
|
+
@factory.mini_css_extract(&block)
|
124
|
+
else
|
125
|
+
mini_css_extract_opts = { opinion: @factory.opinion_mini_css_extract } if mini_css_extract_opts.empty?
|
126
|
+
@factory.mini_css_extract(**mini_css_extract_opts)
|
127
|
+
end
|
128
|
+
|
129
|
+
@webpack_rc.root_scope.require_mini_css_extract_plugin = true
|
130
|
+
end
|
131
|
+
|
132
|
+
write_webpack_rc
|
133
|
+
|
134
|
+
self
|
135
|
+
end
|
136
|
+
alias plugin_split_css plugin_mini_css_extract
|
137
|
+
|
138
|
+
# # Set a property value in the webpack_config
|
139
|
+
# def set(key, value)
|
140
|
+
# load
|
141
|
+
|
142
|
+
# @webpack_config[key] = value
|
143
|
+
|
144
|
+
# write
|
145
|
+
|
146
|
+
# self
|
147
|
+
# end
|
148
|
+
|
149
|
+
# -----------------------------------
|
150
|
+
# Helpers
|
151
|
+
# -----------------------------------
|
152
|
+
|
153
|
+
# Debug method to open the webpack_config file in vscode
|
154
|
+
# ToDo: Maybe remove
|
155
|
+
def vscode
|
156
|
+
puts "cd #{target_folder}"
|
157
|
+
puts webpack_rc_file
|
158
|
+
rc "code #{webpack_rc_file}"
|
159
|
+
|
160
|
+
self
|
161
|
+
end
|
162
|
+
|
163
|
+
def pause(seconds = 1)
|
164
|
+
sleep(seconds)
|
165
|
+
|
166
|
+
self
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
# Load the existing .webpack-rc.json into memory
|
172
|
+
def load_webpack_rc
|
173
|
+
raise KBuilder::Webpack5::Error, '.webpack-rc.json does not exist' unless File.exist?(webpack_rc_file)
|
174
|
+
|
175
|
+
content = File.read(webpack_rc_file)
|
176
|
+
@webpack_rc = JSON.parse(content, object_class: KBuilder::Webpack5::JsonData)
|
177
|
+
|
178
|
+
self
|
179
|
+
end
|
180
|
+
|
181
|
+
def write_webpack_rc
|
182
|
+
content = JSON.pretty_generate(@webpack_rc.as_json)
|
183
|
+
|
184
|
+
FileUtils.mkdir_p(File.dirname(webpack_rc_file))
|
185
|
+
|
186
|
+
File.write(webpack_rc_file, content)
|
187
|
+
|
188
|
+
self
|
189
|
+
end
|
190
|
+
|
191
|
+
def ensure_plugins
|
192
|
+
@webpack_rc.plugins = OpenStruct.new if @webpack_rc.plugins.nil?
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KBuilder
|
4
|
+
module Webpack5
|
5
|
+
# Factory helps give shape to the JSON structure.
|
6
|
+
#
|
7
|
+
# Helps: Because the underlying structure is a typeless and contractless
|
8
|
+
# OpenStruct, the developer could put any values they like in here.
|
9
|
+
# This factory helps to articulate the JSON contract
|
10
|
+
class WebpackJsonFactory
|
11
|
+
def self.build_from_json(json)
|
12
|
+
# ToDo, build up the WebStruct by applying each JSON structure
|
13
|
+
end
|
14
|
+
|
15
|
+
# rubocop:disable Metrics/ParameterLists
|
16
|
+
def self.webpack(
|
17
|
+
settings: WebpackJsonFactory.settings,
|
18
|
+
root_scope: WebpackJsonFactory.root_scope,
|
19
|
+
entry: nil,
|
20
|
+
entries: nil,
|
21
|
+
plugins: nil,
|
22
|
+
dev_server: nil
|
23
|
+
)
|
24
|
+
obj = KBuilder::Webpack5::JsonData.new
|
25
|
+
|
26
|
+
obj.root_scope = root_scope
|
27
|
+
obj.entry = entry unless entry.nil?
|
28
|
+
obj.entries = entries unless entries.nil?
|
29
|
+
obj.dev_server = dev_server unless dev_server.nil?
|
30
|
+
obj.plugins = plugins unless plugins.nil?
|
31
|
+
obj.settings = settings
|
32
|
+
|
33
|
+
yield obj if block_given?
|
34
|
+
|
35
|
+
obj
|
36
|
+
end
|
37
|
+
# rubocop:enable Metrics/ParameterLists
|
38
|
+
|
39
|
+
def self.root_scope
|
40
|
+
obj = KBuilder::Webpack5::JsonData.new
|
41
|
+
|
42
|
+
obj.require_path = false
|
43
|
+
obj.require_webpack = false
|
44
|
+
obj.require_mini_css_extract_plugin = false
|
45
|
+
obj.require_html_webpack_plugin = false
|
46
|
+
obj.require_workbox_webpack_plugin = false
|
47
|
+
obj.require_autoprefixer = false
|
48
|
+
obj.require_precss = false
|
49
|
+
|
50
|
+
yield obj if block_given?
|
51
|
+
|
52
|
+
obj
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.opinion_mode
|
56
|
+
lambda { |json|
|
57
|
+
json.mode = 'development'
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
# https://webpack.js.org/configuration/mode/
|
62
|
+
def self.mode(opinion: nil, **opts)
|
63
|
+
obj = KBuilder::Webpack5::JsonData.new
|
64
|
+
|
65
|
+
# Let the software lead/architect's opinion decide default configuration
|
66
|
+
opinion&.call(obj)
|
67
|
+
|
68
|
+
obj.mode = opts[:mode] unless opts[:mode].nil?
|
69
|
+
|
70
|
+
yield obj if block_given?
|
71
|
+
|
72
|
+
obj
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.opinion_entry
|
76
|
+
lambda { |json|
|
77
|
+
json.entry = './src'
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
# https://webpack.js.org/concepts/entry-points/
|
82
|
+
def self.entry(opinion: nil, **opts)
|
83
|
+
obj = KBuilder::Webpack5::JsonData.new
|
84
|
+
|
85
|
+
# Let the software lead/architect's opinion decide default configuration
|
86
|
+
opinion&.call(obj)
|
87
|
+
|
88
|
+
# https://webpack.js.org/configuration/
|
89
|
+
obj.entry = opts[:entry] unless opts[:entry].nil?
|
90
|
+
|
91
|
+
yield obj if block_given?
|
92
|
+
|
93
|
+
obj
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.opinion_entries
|
97
|
+
lambda { |json|
|
98
|
+
json.home = './src/home.js'
|
99
|
+
json.about = './src/about.js'
|
100
|
+
}
|
101
|
+
end
|
102
|
+
|
103
|
+
# https://webpack.js.org/concepts/entry-points/
|
104
|
+
def self.entries(opinion: nil)
|
105
|
+
obj = KBuilder::Webpack5::JsonData.new
|
106
|
+
|
107
|
+
# Let the software lead/architect's opinion decide default configuration
|
108
|
+
opinion&.call(obj)
|
109
|
+
|
110
|
+
yield obj if block_given?
|
111
|
+
|
112
|
+
obj
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.settings
|
116
|
+
obj = KBuilder::Webpack5::JsonData.new
|
117
|
+
|
118
|
+
# Render the TIPS
|
119
|
+
|
120
|
+
obj.tips = false
|
121
|
+
|
122
|
+
yield obj if block_given?
|
123
|
+
|
124
|
+
obj
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.opinion_dev_server
|
128
|
+
lambda { |json|
|
129
|
+
json.open = true
|
130
|
+
json.localhost = 'localhost'
|
131
|
+
}
|
132
|
+
end
|
133
|
+
|
134
|
+
# https://webpack.js.org/configuration/dev-server/
|
135
|
+
# https://github.com/webpack/webpack-dev-server
|
136
|
+
# rubocop:disable Metrics/AbcSize
|
137
|
+
def self.dev_server(opinion: nil, **opts)
|
138
|
+
obj = KBuilder::Webpack5::JsonData.new
|
139
|
+
|
140
|
+
# Let the software lead/architect's opinion decide default configuration
|
141
|
+
opinion&.call(obj)
|
142
|
+
|
143
|
+
# https://github.com/webpack/webpack-dev-server/tree/master/examples/cli/public
|
144
|
+
obj.open = opts[:open] unless opts[:open].nil? # true
|
145
|
+
obj.localhost = opts[:localhost] unless opts[:localhost].nil? # localhost
|
146
|
+
|
147
|
+
# https://github.com/webpack/webpack-dev-server/tree/master/examples/cli/watch-static
|
148
|
+
obj.static = opts[:static] unless opts[:static].nil? # static: ['assets', 'css']
|
149
|
+
|
150
|
+
yield obj if block_given?
|
151
|
+
|
152
|
+
# Samples
|
153
|
+
# devServer: {
|
154
|
+
# open: true,
|
155
|
+
# host: 'localhost'
|
156
|
+
# }
|
157
|
+
#
|
158
|
+
# devServer: {
|
159
|
+
# contentBase: path.join(__dirname, 'dist'),
|
160
|
+
# compress: true,
|
161
|
+
# port: 9000,
|
162
|
+
# },
|
163
|
+
|
164
|
+
# TIPS
|
165
|
+
# - If you're having trouble, navigating to the /webpack-dev-server route will show where files are served. For example, http://localhost:9000/webpack-dev-server.
|
166
|
+
# - If you want to manually recompile the bundle, navigating to the /invalidate route will invalidate the current compilation of the bundle and recompile it for you via webpack-dev-middleware.
|
167
|
+
# Depending on your configuration, URL may look like http://localhost:9000/invalidate.
|
168
|
+
# - HTML template is required to serve the bundle, usually it is an index.html file. Make sure that script references are added into HTML, webpack-dev-server doesn't inject them automatically.
|
169
|
+
|
170
|
+
obj
|
171
|
+
end
|
172
|
+
# rubocop:enable Metrics/AbcSize
|
173
|
+
|
174
|
+
def self.opinion_mini_css_extract
|
175
|
+
lambda { |json|
|
176
|
+
json.filename = 'main.[contenthash].css'
|
177
|
+
}
|
178
|
+
end
|
179
|
+
|
180
|
+
# https://webpack.js.org/plugins/mini-css-extract-plugin/
|
181
|
+
def self.mini_css_extract(opinion: nil, **opts)
|
182
|
+
obj = KBuilder::Webpack5::JsonData.new
|
183
|
+
|
184
|
+
# Let the software lead/architect's opinion decide default configuration
|
185
|
+
opinion&.call(obj)
|
186
|
+
|
187
|
+
obj.filename = opts[:filename] unless opts[:filename].nil?
|
188
|
+
|
189
|
+
yield obj if block_given?
|
190
|
+
|
191
|
+
obj
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: k_builder-webpack5
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Cruwys
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: k_builder-package_json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.0'
|
27
41
|
description: " KBuilder-Webpack5 provides various fluent builders for building
|
28
42
|
webpack configuration file\n"
|
29
43
|
email:
|
@@ -36,6 +50,9 @@ files:
|
|
36
50
|
- ".gitignore"
|
37
51
|
- ".rspec"
|
38
52
|
- ".rubocop.yml"
|
53
|
+
- ".templates/sample.txt"
|
54
|
+
- ".templates/web-project/.gitignore"
|
55
|
+
- ".templates/webpack.config.js.txt"
|
39
56
|
- CODE_OF_CONDUCT.md
|
40
57
|
- Gemfile
|
41
58
|
- Guardfile
|
@@ -53,7 +70,10 @@ files:
|
|
53
70
|
- hooks/update-version
|
54
71
|
- k_builder-webpack5.gemspec
|
55
72
|
- lib/k_builder/webpack5.rb
|
73
|
+
- lib/k_builder/webpack5/json_data.rb
|
56
74
|
- lib/k_builder/webpack5/version.rb
|
75
|
+
- lib/k_builder/webpack5/webpack_builder.rb
|
76
|
+
- lib/k_builder/webpack5/webpack_json_factory.rb
|
57
77
|
homepage: http://appydave.com/gems/k-builder-webpack5
|
58
78
|
licenses:
|
59
79
|
- MIT
|