bridgetown-core 1.0.0.alpha9 → 1.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -0
  3. data/bin/bridgetown +6 -1
  4. data/bridgetown-core.gemspec +4 -3
  5. data/lib/bridgetown-core/collection.rb +6 -6
  6. data/lib/bridgetown-core/commands/base.rb +18 -18
  7. data/lib/bridgetown-core/commands/build.rb +1 -1
  8. data/lib/bridgetown-core/commands/clean.rb +2 -2
  9. data/lib/bridgetown-core/commands/console.rb +1 -0
  10. data/lib/bridgetown-core/commands/esbuild/esbuild.config.js +27 -0
  11. data/lib/bridgetown-core/commands/esbuild/esbuild.defaults.js.erb +216 -0
  12. data/lib/bridgetown-core/commands/esbuild/migrate-from-webpack.rb +47 -0
  13. data/lib/bridgetown-core/commands/esbuild/setup.rb +4 -0
  14. data/lib/bridgetown-core/commands/esbuild/update.rb +4 -0
  15. data/lib/bridgetown-core/commands/esbuild.rb +83 -0
  16. data/lib/bridgetown-core/commands/new.rb +80 -10
  17. data/lib/bridgetown-core/commands/plugins.rb +1 -1
  18. data/lib/bridgetown-core/commands/webpack/enable-postcss.rb +1 -1
  19. data/lib/bridgetown-core/commands/webpack/update.rb +3 -3
  20. data/lib/bridgetown-core/commands/webpack/webpack.defaults.js.erb +1 -1
  21. data/lib/bridgetown-core/commands/webpack.rb +3 -3
  22. data/lib/bridgetown-core/component.rb +9 -3
  23. data/lib/bridgetown-core/concerns/site/configurable.rb +6 -0
  24. data/lib/bridgetown-core/concerns/site/content.rb +4 -4
  25. data/lib/bridgetown-core/concerns/site/extensible.rb +8 -0
  26. data/lib/bridgetown-core/concerns/site/processable.rb +23 -4
  27. data/lib/bridgetown-core/concerns/site/ssr.rb +2 -17
  28. data/lib/bridgetown-core/configurations/minitesting.rb +1 -1
  29. data/lib/bridgetown-core/configurations/purgecss.rb +2 -1
  30. data/lib/bridgetown-core/configurations/render/render.yaml.erb +3 -0
  31. data/lib/bridgetown-core/configurations/stimulus.rb +41 -12
  32. data/lib/bridgetown-core/configurations/tailwindcss/css_imports.css +5 -0
  33. data/lib/bridgetown-core/configurations/tailwindcss.rb +31 -2
  34. data/lib/bridgetown-core/configurations/turbo/turbo_transitions.js +48 -0
  35. data/lib/bridgetown-core/configurations/turbo.rb +15 -5
  36. data/lib/bridgetown-core/configurations/vercel/vercel.json +45 -0
  37. data/lib/bridgetown-core/configurations/vercel/vercel_url.rb +12 -0
  38. data/lib/bridgetown-core/configurations/vercel.rb +4 -0
  39. data/lib/bridgetown-core/converters/erb_templates.rb +7 -9
  40. data/lib/bridgetown-core/converters/ruby_templates.rb +1 -1
  41. data/lib/bridgetown-core/converters/serbea_templates.rb +5 -8
  42. data/lib/bridgetown-core/drops/drop.rb +1 -1
  43. data/lib/bridgetown-core/drops/resource_drop.rb +28 -5
  44. data/lib/bridgetown-core/errors.rb +21 -0
  45. data/lib/bridgetown-core/generators/prototype_generator.rb +3 -3
  46. data/lib/bridgetown-core/helpers.rb +3 -2
  47. data/lib/bridgetown-core/hooks.rb +51 -20
  48. data/lib/bridgetown-core/model/base.rb +24 -1
  49. data/lib/bridgetown-core/model/origin.rb +4 -6
  50. data/lib/bridgetown-core/model/repo_origin.rb +48 -0
  51. data/lib/bridgetown-core/rack/boot.rb +5 -9
  52. data/lib/bridgetown-core/rack/roda.rb +4 -5
  53. data/lib/bridgetown-core/rack/routes.rb +44 -10
  54. data/lib/bridgetown-core/rack/static_indexes.rb +2 -0
  55. data/lib/bridgetown-core/resource/base.rb +3 -1
  56. data/lib/bridgetown-core/ruby_template_view.rb +11 -0
  57. data/lib/bridgetown-core/site.rb +5 -0
  58. data/lib/bridgetown-core/tags/{webpack_path.rb → asset_path.rb} +7 -9
  59. data/lib/bridgetown-core/tasks/bridgetown_tasks.rake +2 -1
  60. data/lib/bridgetown-core/utils/loaders_manager.rb +17 -0
  61. data/lib/bridgetown-core/utils.rb +88 -30
  62. data/lib/bridgetown-core/version.rb +1 -1
  63. data/lib/bridgetown-core/watcher.rb +74 -70
  64. data/lib/bridgetown-core.rb +1 -0
  65. data/lib/site_template/Gemfile.erb +17 -11
  66. data/lib/site_template/README.md +2 -2
  67. data/lib/site_template/{Rakefile → Rakefile.erb} +15 -0
  68. data/lib/site_template/TEMPLATES/erb/_components/shared/navbar.erb +11 -0
  69. data/lib/site_template/TEMPLATES/erb/_components/shared/navbar.rb +5 -0
  70. data/lib/site_template/TEMPLATES/erb/_layouts/default.erb +15 -0
  71. data/lib/site_template/TEMPLATES/erb/_layouts/page.erb +7 -0
  72. data/lib/site_template/TEMPLATES/erb/_layouts/post.erb +7 -0
  73. data/lib/site_template/TEMPLATES/erb/_partials/_footer.erb +3 -0
  74. data/lib/site_template/TEMPLATES/erb/_partials/_head.erb +10 -0
  75. data/lib/site_template/{src → TEMPLATES/liquid}/_components/footer.liquid +0 -0
  76. data/lib/site_template/TEMPLATES/liquid/_components/head.liquid +10 -0
  77. data/lib/site_template/TEMPLATES/liquid/_components/navbar.liquid +11 -0
  78. data/lib/site_template/{src → TEMPLATES/liquid}/_layouts/default.liquid +2 -2
  79. data/lib/site_template/{src → TEMPLATES/liquid}/_layouts/page.liquid +0 -0
  80. data/lib/site_template/{src → TEMPLATES/liquid}/_layouts/post.liquid +0 -0
  81. data/lib/site_template/TEMPLATES/serbea/_components/shared/navbar.rb +5 -0
  82. data/lib/site_template/TEMPLATES/serbea/_components/shared/navbar.serb +11 -0
  83. data/lib/site_template/TEMPLATES/serbea/_layouts/default.serb +15 -0
  84. data/lib/site_template/TEMPLATES/serbea/_layouts/page.serb +7 -0
  85. data/lib/site_template/TEMPLATES/serbea/_layouts/post.serb +7 -0
  86. data/lib/site_template/TEMPLATES/serbea/_partials/_footer.serb +3 -0
  87. data/lib/site_template/TEMPLATES/serbea/_partials/_head.serb +10 -0
  88. data/lib/site_template/frontend/javascript/index.js.erb +7 -3
  89. data/lib/site_template/frontend/styles/index.css +71 -6
  90. data/lib/site_template/package.json.erb +24 -9
  91. data/lib/site_template/ruby-version.erb +1 -0
  92. data/lib/site_template/server/roda_app.rb +5 -3
  93. data/lib/site_template/server/routes/hello.rb.sample +1 -1
  94. data/lib/site_template/src/images/logo.svg +91 -0
  95. data/lib/site_template/src/index.md.erb +22 -0
  96. data/lib/site_template/src/posts.md.erb +28 -0
  97. metadata +70 -22
  98. data/lib/bridgetown-core/configurations/swup.rb +0 -37
  99. data/lib/site_template/frontend/styles/index.scss +0 -17
  100. data/lib/site_template/src/_components/head.liquid +0 -10
  101. data/lib/site_template/src/_components/navbar.liquid +0 -5
  102. data/lib/site_template/src/_layouts/home.liquid +0 -7
  103. data/lib/site_template/src/images/.keep +0 -1
  104. data/lib/site_template/src/index.md +0 -7
  105. data/lib/site_template/src/posts.md +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 913ec78ef91ecd5b8e9f56777878f68bcb31625c9af575597bf56a0bf025f252
4
- data.tar.gz: f1e4b3292c9f4edf1ebf0318ba2d2751d0b220dc7346536014d4328772cb1879
3
+ metadata.gz: e6a9d2528b695e8e0cf1016e12d9ef4f83ea3983568db4b20e740f5256024990
4
+ data.tar.gz: 8cfdc7484ba7f011cac11708667eb93e3fa27400269b7a548fc1cbc80dee37c1
5
5
  SHA512:
6
- metadata.gz: f8affaf97ca29c3e093bf0562259bd9d878ccc216ba63a52ceb3388cfd735b92a32add0dadb8a2b748b7ba416c5a9167c0f6983dc5705b9622fbac445f4fd352
7
- data.tar.gz: 56917b674308b501407ab2a4eb7882cc679794e895291db31f4bc80724c1d1e8cbcabd25b86784300e91b0ec3a0cc80bc8789f01730615965fe4b267b8a6570f
6
+ metadata.gz: 98523ff2520d3428505ba743408fd20f4672cdfdebdb50fe914b025b7e63e6d661f923cdd6ee0d27c9f8cd126fc08b8b522206461b5e5862b6ddc7b30fcfe02c
7
+ data.tar.gz: 795673a2a1e3b44aa0624fbf1c7bb0e9bac35d2f3882dd45d309f70346777896a9ed75c1993f186f2a3adfc94df0d5c69e0beb71a7fbeaea3aa7ac5bf774400f
data/.rubocop.yml CHANGED
@@ -12,6 +12,7 @@ AllCops:
12
12
  - tmp/**/*
13
13
  - test/source/**/*
14
14
  - test/resources/src/_pages/*.rb
15
+ - lib/site_template/TEMPLATES/**/*
15
16
  - lib/site_template/Rakefile
16
17
  - lib/site_template/config.ru
17
18
  - lib/site_template/config/**/*
@@ -31,6 +32,10 @@ Performance/CollectionLiteralInLoop:
31
32
  Exclude:
32
33
  - test/test_filters.rb
33
34
 
35
+ Style/OpenStructUse:
36
+ Exclude:
37
+ - test/**/*.rb
38
+
34
39
  Style/StringConcatenation:
35
40
  Exclude:
36
41
  - test/test_apply_command.rb
data/bin/bridgetown CHANGED
@@ -34,4 +34,9 @@ end
34
34
  ENV["RACK_ENV"] = ENV["BRIDGETOWN_ENV"]
35
35
 
36
36
  require "bridgetown-core/commands/base"
37
- Bridgetown::Commands::Base.start unless output_version
37
+ begin
38
+ Bridgetown::Commands::Base.start unless output_version
39
+ rescue StandardError => e
40
+ Bridgetown::Errors.print_build_error(e)
41
+ exit(false)
42
+ end
@@ -30,15 +30,15 @@ Gem::Specification.new do |s|
30
30
 
31
31
  s.required_ruby_version = ">= 2.7.0"
32
32
 
33
- s.add_runtime_dependency("activemodel", "~> 6.0")
34
- s.add_runtime_dependency("activesupport", "~> 6.0")
33
+ s.add_runtime_dependency("activemodel", [">= 6.0", "< 8.0"])
34
+ s.add_runtime_dependency("activesupport", [">= 6.0", "< 8.0"])
35
35
  s.add_runtime_dependency("addressable", "~> 2.4")
36
36
  s.add_runtime_dependency("amazing_print", "~> 1.2")
37
37
  s.add_runtime_dependency("colorator", "~> 1.0")
38
38
  s.add_runtime_dependency("erubi", "~> 1.9")
39
39
  s.add_runtime_dependency("faraday", "~> 1.0")
40
40
  s.add_runtime_dependency("faraday_middleware", "~> 1.0")
41
- s.add_runtime_dependency("hash_with_dot_access", "~> 1.0")
41
+ s.add_runtime_dependency("hash_with_dot_access", "~> 1.2")
42
42
  s.add_runtime_dependency("i18n", "~> 1.0")
43
43
  s.add_runtime_dependency("kramdown", "~> 2.1")
44
44
  s.add_runtime_dependency("kramdown-parser-gfm", "~> 1.0")
@@ -53,4 +53,5 @@ Gem::Specification.new do |s|
53
53
  s.add_runtime_dependency("thor", "~> 1.1")
54
54
  s.add_runtime_dependency("tilt", "~> 2.0")
55
55
  s.add_runtime_dependency("webrick", "~> 1.7")
56
+ s.add_runtime_dependency("zeitwerk", "~> 2.5")
56
57
  end
@@ -297,12 +297,6 @@ module Bridgetown
297
297
  resources << resource if site.config.unpublished || resource.published?
298
298
  end
299
299
 
300
- private
301
-
302
- def container
303
- @container ||= site.config["collections_dir"]
304
- end
305
-
306
300
  def sort_resources!
307
301
  if metadata["sort_by"].is_a?(String)
308
302
  sort_resources_by_key!
@@ -312,6 +306,12 @@ module Bridgetown
312
306
  resources.reverse! if metadata.sort_direction == "descending"
313
307
  end
314
308
 
309
+ private
310
+
311
+ def container
312
+ @container ||= site.config["collections_dir"]
313
+ end
314
+
315
315
  # A custom sort function based on Schwartzian transform
316
316
  # Refer https://byparker.com/blog/2017/schwartzian-transform-faster-sorting/ for details
317
317
  def sort_resources_by_key!
@@ -34,41 +34,42 @@ module Bridgetown
34
34
  rake.display_tasks_and_comments
35
35
  end
36
36
 
37
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Style/GlobalVars
37
+ def load_rake_tasks(rake)
38
+ rake.load_rakefile
39
+ tasks = rake.instance_variable_get(:@tasks)
40
+ rake.instance_variable_set(:@tasks, tasks.reject do |_k, v|
41
+ v.locations.first&.include?("/lib/rails/tasks/") ||
42
+ v.locations.first&.include?("/lib/rake/dsl_definition")
43
+ end)
44
+ end
45
+
46
+ # rubocop:disable Style/GlobalVars
38
47
  def handle_no_command_error(cmd, _has_namespace = $thor_runner)
39
48
  require "rake"
40
49
  Rake::TaskManager.record_task_metadata = true
41
50
 
42
51
  Rake.with_application do |rake|
43
- rake.instance_variable_set(:@name, "bridgetown")
44
52
  rake.standard_exception_handling do
45
53
  rakefile, _location = rake.find_rakefile_location
46
54
  unless rakefile
47
- puts "No Rakefile found (searching: #{rake.class::DEFAULT_RAKEFILES.join(", ")})\n"
55
+ puts "No Rakefile found (searching: #{rake.class::DEFAULT_RAKEFILES.join(", ")})\n\n" # rubocop:disable Layout/LineLength
48
56
  new.invoke("help")
49
57
  return # rubocop:disable Lint/NonLocalExitFromIterator
50
58
  end
51
- rake.load_rakefile
52
- rake.top_level
53
- end
54
- cmd = cmd.split("[")
55
- args = []
56
- if cmd[1]
57
- args = cmd[1].gsub("\\,", "__COMMA__").delete_suffix!("]").split(",").map do |item|
58
- item.gsub("__COMMA__", ",")
59
- end
59
+ rake.init("bridgetown")
60
+ load_rake_tasks(rake)
60
61
  end
61
62
 
62
- if Rake::Task.task_defined?(cmd[0])
63
- Rake::Task[cmd[0]].invoke(*args)
63
+ if Rake::Task.task_defined?(cmd.split("[")[0])
64
+ rake.top_level
64
65
  else
65
- puts "Unknown task: #{cmd[0]}\n\nHere's a list of tasks you can run:"
66
+ puts "Unknown task: #{cmd.split("[")[0]}\n\nHere's a list of tasks you can run:"
66
67
  display_rake_tasks(rake)
67
68
  end
68
69
  end
69
70
  end
70
71
  end
71
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Style/GlobalVars
72
+ # rubocop:enable Style/GlobalVars
72
73
 
73
74
  desc "dream", "There's a place where that idea still exists as a reality"
74
75
  def dream
@@ -101,8 +102,7 @@ module Bridgetown
101
102
  rakefile, _location = rake.find_rakefile_location
102
103
  return unless rakefile # rubocop:disable Lint/NonLocalExitFromIterator
103
104
 
104
- rake.load_rakefile
105
- rake.top_level
105
+ self.class.load_rake_tasks(rake)
106
106
  puts "Available Rake Tasks:"
107
107
  self.class.display_rake_tasks(rake)
108
108
  end
@@ -77,7 +77,7 @@ module Bridgetown
77
77
  end
78
78
  Bridgetown.logger.info "Generating…"
79
79
  @site.process
80
- Bridgetown.logger.info "Done! 🎉", "#{"Completed".green} in less than" \
80
+ Bridgetown.logger.info "Done! 🎉", "#{"Completed".bold.green} in less than" \
81
81
  " #{(Time.now - t).ceil(2)} seconds."
82
82
 
83
83
  return unless config_options[:using_puma]
@@ -21,12 +21,12 @@ module Bridgetown
21
21
  destination = config["destination"]
22
22
  metadata_file = File.join(config["root_dir"], ".bridgetown-metadata")
23
23
  cache_dir = File.join(config["root_dir"], config["cache_dir"])
24
- webpack_dir = File.join(config["root_dir"], ".bridgetown-webpack")
24
+ bundling_dir = File.join(config["root_dir"], ".bridgetown-cache", "frontend-bundling")
25
25
 
26
26
  remove(destination, checker_func: :directory?)
27
27
  remove(metadata_file, checker_func: :file?)
28
28
  remove(cache_dir, checker_func: :directory?)
29
- remove(webpack_dir, checker_func: :directory?)
29
+ remove(bundling_dir, checker_func: :directory?)
30
30
  end
31
31
 
32
32
  protected
@@ -18,6 +18,7 @@ module Bridgetown
18
18
  Bridgetown::Hooks.clear_reloadable_hooks
19
19
  site.plugin_manager.reload_plugin_files
20
20
  site.loaders_manager.reload_loaders
21
+ Bridgetown::Hooks.trigger :site, :post_reload, site
21
22
 
22
23
  ConsoleMethods.site_reset(site)
23
24
  end
@@ -0,0 +1,27 @@
1
+ const build = require("./config/esbuild.defaults.js")
2
+
3
+ // Update this if you need to configure a destination folder other than `output`
4
+ const outputFolder = "output"
5
+
6
+ // You can customize this as you wish, perhaps to add new esbuild plugins.
7
+ //
8
+ // Eg:
9
+ //
10
+ // ```
11
+ // const path = require("path")
12
+ // const esbuildCopy = require('esbuild-plugin-copy').default
13
+ // const esbuildOptions = {
14
+ // plugins: [
15
+ // esbuildCopy({
16
+ // assets: {
17
+ // from: [path.resolve(__dirname, 'node_modules/somepackage/files/*')],
18
+ // to: [path.resolve(__dirname, 'output/_bridgetown/somepackage/files')],
19
+ // },
20
+ // verbose: false
21
+ // }),
22
+ // ]
23
+ // }
24
+ // ```
25
+ const esbuildOptions = {}
26
+
27
+ build(outputFolder, esbuildOptions)
@@ -0,0 +1,216 @@
1
+ // This file is created and managed by Bridgetown.
2
+ // Instead of editing this file, add your overrides to `esbuild.config.js`
3
+ //
4
+ // To update this file to the latest version provided by Bridgetown,
5
+ // run `bridgetown esbuild update`. Any changes to this file will be overwritten
6
+ // when an update is applied hence we strongly recommend adding overrides to
7
+ // `esbuild.config.js` instead of editing this file.
8
+ //
9
+ // Shipped with Bridgetown v<%= Bridgetown::VERSION %>
10
+
11
+ const path = require("path")
12
+ const fsLib = require("fs")
13
+ const fs = fsLib.promises
14
+ const glob = require("glob")
15
+ const postcss = require("postcss")
16
+ const postCssImport = require("postcss-import")
17
+ const readCache = require("read-cache")
18
+
19
+ // Glob plugin derived from:
20
+ // https://github.com/thomaschaaf/esbuild-plugin-import-glob
21
+ // https://github.com/xiaohui-zhangxh/jsbundling-rails/commit/b15025dcc20f664b2b0eb238915991afdbc7cb58
22
+ const importGlobPlugin = () => ({
23
+ name: "import-glob",
24
+ setup: (build) => {
25
+ build.onResolve({ filter: /\*/ }, async (args) => {
26
+ if (args.resolveDir === "") {
27
+ return; // Ignore unresolvable paths
28
+ }
29
+
30
+ const adjustedPath = args.path.replace(/^bridgetownComponents\//, "../../src/_components/")
31
+
32
+ return {
33
+ path: adjustedPath,
34
+ namespace: "import-glob",
35
+ pluginData: {
36
+ path: adjustedPath,
37
+ resolveDir: args.resolveDir,
38
+ },
39
+ }
40
+ })
41
+
42
+ build.onLoad({ filter: /.*/, namespace: "import-glob" }, async (args) => {
43
+ const files = glob.sync(args.pluginData.path, {
44
+ cwd: args.pluginData.resolveDir,
45
+ }).sort()
46
+
47
+ const importerCode = `
48
+ ${files
49
+ .map((module, index) => `import * as module${index} from '${module}'`)
50
+ .join(';')}
51
+ const modules = {${files
52
+ .map((module, index) => `
53
+ "${module.replace("../../src/_components/", "")}": module${index},`)
54
+ .join("")}
55
+ };
56
+ export default modules;
57
+ `
58
+
59
+ return { contents: importerCode, resolveDir: args.pluginData.resolveDir }
60
+ })
61
+ },
62
+ })
63
+
64
+ const postCssPlugin = (options) => ({
65
+ name: "postcss",
66
+ async setup(build) {
67
+ // Process .css files with PostCSS
68
+ build.onLoad({ filter: /\.(css)$/ }, async (args) => {
69
+ const additionalFilePaths = []
70
+ const css = await fs.readFile(args.path, "utf8")
71
+
72
+ // Configure import plugin so PostCSS can properly resolve `@import`ed CSS files
73
+ const importPlugin = postCssImport({
74
+ filter: itemPath => {
75
+ // We'll want to track any imports later when in watch mode
76
+ additionalFilePaths.push(path.resolve(path.dirname(args.path), itemPath))
77
+ return true
78
+ },
79
+ load: async filename => {
80
+ let contents = await readCache(filename, "utf-8")
81
+ const filedir = path.dirname(filename)
82
+
83
+ // We need to transform `url(...)` in imported CSS so the filepaths are properly
84
+ // relative to the entrypoint. Seems icky to have to hack this! C'est la vie...
85
+ contents = contents.replace(/url\(['"]?\.\/(.*?)['"]?\)/g, (_match, p1) => {
86
+ const relpath = path.relative(args.path, path.resolve(filedir, p1)).replace(/^\.\.\//, "")
87
+ return `url("${relpath}")`
88
+ })
89
+ return contents
90
+ }
91
+ })
92
+
93
+ // Process the file through PostCSS
94
+ const result = await postcss([importPlugin, ...options.plugins]).process(css, {
95
+ map: true,
96
+ ...options.options,
97
+ from: args.path,
98
+ });
99
+
100
+ return {
101
+ contents: result.css,
102
+ loader: "css",
103
+ watchFiles: [args.path, ...additionalFilePaths],
104
+ }
105
+ })
106
+ },
107
+ })
108
+
109
+ // Set up defaults and generate frontend bundling manifest file
110
+ const bridgetownPreset = (outputFolder) => ({
111
+ name: "bridgetownPreset",
112
+ async setup(build) {
113
+ // Ensure any imports anywhere starting with `/` are left verbatim
114
+ // so they can be used in-browser for actual `src` repo files
115
+ build.onResolve({ filter: /^\// }, args => {
116
+ return { path: args.path, external: true }
117
+ })
118
+
119
+ build.onStart(() => {
120
+ console.log("esbuild: frontend bundling started...")
121
+ })
122
+
123
+ // Generate the final output manifest
124
+ build.onEnd(async (result) => {
125
+ if (!result.metafile) {
126
+ console.warn("esbuild: build process error, cannot write manifest")
127
+ return
128
+ }
129
+
130
+ const manifest = {}
131
+ const entrypoints = []
132
+
133
+ // We don't need `frontend/` cluttering up everything
134
+ const stripPrefix = (str) => str.replace(/^frontend\//, "")
135
+
136
+ // For calculating the file size of bundle output
137
+ const fileSize = (path) => {
138
+ const { size } = fsLib.statSync(path)
139
+ const i = Math.floor(Math.log(size) / Math.log(1024))
140
+ return (size / Math.pow(1024, i)).toFixed(2) * 1 + ['B', 'KB', 'MB', 'GB', 'TB'][i]
141
+ }
142
+
143
+ // Let's loop through all the various outputs
144
+ for (const key in result.metafile.outputs) {
145
+ const value = result.metafile.outputs[key]
146
+ const inputs = Object.keys(value.inputs)
147
+ const pathShortener = new RegExp(`^${outputFolder}\\/_bridgetown\\/static\\/`, "g")
148
+ const outputPath = key.replace(pathShortener, "")
149
+
150
+ if (value.entryPoint) {
151
+ // We have an entrypoint!
152
+ manifest[stripPrefix(value.entryPoint)] = outputPath
153
+ entrypoints.push([outputPath, fileSize(key)])
154
+ } else if (key.match(/index(\.js)?\.[^-.]*\.css/) && inputs.find(item => item.endsWith("index.css"))) {
155
+ // Special treatment for index.css
156
+ manifest[stripPrefix(inputs.find(item => item.endsWith("index.css")))] = outputPath
157
+ entrypoints.push([outputPath, fileSize(key)])
158
+ } else if (inputs.length > 0) {
159
+ // Naive implementation, we'll just grab the first input and hope it's accurate
160
+ manifest[stripPrefix(inputs[0])] = outputPath
161
+ }
162
+ }
163
+
164
+ const manifestFolder = path.join(process.cwd(), ".bridgetown-cache", "frontend-bundling")
165
+ await fs.mkdir(manifestFolder, { recursive: true })
166
+ await fs.writeFile(path.join(manifestFolder, "manifest.json"), JSON.stringify(manifest))
167
+
168
+ console.log("esbuild: frontend bundling complete!")
169
+ console.log("esbuild: entrypoints processed:")
170
+ entrypoints.forEach(entrypoint => {
171
+ const [entrypointName, entrypointSize] = entrypoint
172
+ console.log(` - ${entrypointName}: ${entrypointSize}`)
173
+ })
174
+ })
175
+ }
176
+ })
177
+
178
+ // Load the PostCSS config from postcss.config.js or whatever else is a supported location/format
179
+ const postcssrc = require("postcss-load-config")
180
+ const postCssConfig = postcssrc.sync()
181
+
182
+ module.exports = (outputFolder, esbuildOptions) => {
183
+ esbuildOptions.plugins = esbuildOptions.plugins || []
184
+ // Add the PostCSS & glob plugins to the top of the plugin stack
185
+ esbuildOptions.plugins.unshift(postCssPlugin(postCssConfig))
186
+ esbuildOptions.plugins.unshift(importGlobPlugin())
187
+ // Add the Bridgetown preset to the bottom of the plugin stack
188
+ esbuildOptions.plugins.push(bridgetownPreset(outputFolder))
189
+
190
+ // esbuild, take it away!
191
+ require("esbuild").build({
192
+ bundle: true,
193
+ loader: {
194
+ ".jpg": "file",
195
+ ".png": "file",
196
+ ".gif": "file",
197
+ ".svg": "file",
198
+ ".woff": "file",
199
+ ".woff2": "file",
200
+ ".ttf": "file",
201
+ ".eot": "file",
202
+ },
203
+ resolveExtensions: [".tsx",".ts",".jsx",".js",".css",".json",".js.rb"],
204
+ nodePaths: ["frontend/javascript", "frontend/styles"],
205
+ watch: process.argv.includes("--watch"),
206
+ minify: process.argv.includes("--minify"),
207
+ sourcemap: true,
208
+ target: "es2016",
209
+ entryPoints: ["frontend/javascript/index.js"],
210
+ entryNames: "[dir]/[name].[hash]",
211
+ outdir: path.join(process.cwd(), `${outputFolder}/_bridgetown/static`),
212
+ publicPath: "/_bridgetown/static",
213
+ metafile: true,
214
+ ...esbuildOptions,
215
+ }).catch(() => process.exit(1))
216
+ }
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Layout/LineLength
4
+
5
+ if package_json["devDependencies"].key?("sass")
6
+ say "Unable to migrate, project uses Sass. Please migrate to PostCSS first before migrating to esbuild."
7
+ return
8
+ end
9
+
10
+ remove_file "webpack.config.js"
11
+ remove_file "config/webpack.defaults.js"
12
+
13
+ apply find_in_source_paths("setup.rb"), verbose: false
14
+
15
+ default_postcss_config = File.expand_path("../../../site_template/postcss.config.js.erb", __dir__)
16
+ template default_postcss_config, "postcss.config.js"
17
+
18
+ unless Bridgetown.environment.test?
19
+ required_packages = %w(esbuild glob postcss postcss-flexbugs-fixes postcss-preset-env postcss-import postcss-load-config)
20
+ redundant_packages = %w(esbuild-loader webpack webpack-cli webpack-manifest-plugin webpack-merge css-loader file-loader mini-css-extract-plugin postcss-loader)
21
+
22
+ say "Installing required packages"
23
+
24
+ gsub_file "package.json", %r! "postcss-focus-within": "^4.0.0",?!, ""
25
+
26
+ run "yarn add -D #{required_packages.join(" ")}"
27
+
28
+ packages_to_remove = package_json["devDependencies"].slice(*redundant_packages).keys
29
+ unless packages_to_remove.empty?
30
+ confirm = ask "\nThe following packages will be removed: \n\n#{packages_to_remove.join("\n")}\n\nWould you like to continue? [Yn]"
31
+ return unless confirm.casecmp?("Y")
32
+
33
+ run "yarn remove #{packages_to_remove.join(" ")}"
34
+ end
35
+ end
36
+
37
+ gsub_file "Rakefile", %(desc "Build the frontend with Webpack for deployment"), %(desc "Build the frontend with esbuild for deployment")
38
+ gsub_file "Rakefile", %(desc "Watch the frontend with Webpack during development"), %(desc "Watch the frontend with esbuild during development")
39
+ gsub_file "Rakefile", %(sh "yarn run webpack-build"), %(sh "yarn run esbuild")
40
+ gsub_file "Rakefile", %(sh "yarn run webpack-dev --color"), %(sh "yarn run esbuild-dev")
41
+ gsub_file "package.json", %("webpack-build": "webpack --mode production"), %("esbuild": "node esbuild.config.js --minify")
42
+ gsub_file "package.json", %("webpack-dev": "webpack --mode development -w"), %("esbuild-dev": "node esbuild.config.js --watch")
43
+
44
+ say "🎉 Migration steps to esbuild finished!"
45
+ say "Make sure you replace your `webpack_path` helpers with `asset_path` helpers in your templates"
46
+
47
+ # rubocop:enable Layout/LineLength
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ template "esbuild.defaults.js.erb", "config/esbuild.defaults.js"
4
+ copy_file "esbuild.config.js", force: true
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ template "esbuild.defaults.js.erb", "config/esbuild.defaults.js", force: true
4
+ say "🎉 esbuild configuration updated successfully!"
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bridgetown
4
+ module Commands
5
+ class Esbuild < Thor::Group
6
+ include Thor::Actions
7
+ extend Summarizable
8
+
9
+ Registrations.register do
10
+ register(Esbuild, "esbuild", "esbuild ACTION", Esbuild.summary)
11
+ end
12
+
13
+ def self.banner
14
+ "bridgetown esbuild ACTION"
15
+ end
16
+ summary "Perform actions on the Bridgetown esbuild configuration"
17
+
18
+ def self.exit_on_failure?
19
+ true
20
+ end
21
+
22
+ def esbuild
23
+ @logger = Bridgetown.logger
24
+ return show_actions if args.empty?
25
+
26
+ action = args.first
27
+ if supported_actions.include?(action)
28
+ perform action
29
+ else
30
+ @logger.error "Error:".red, "🚨 Please enter a valid action."
31
+ say "\n"
32
+ show_actions
33
+ end
34
+ end
35
+
36
+ def self.source_root
37
+ File.expand_path("./esbuild", __dir__)
38
+ end
39
+
40
+ def self.destination_root
41
+ config.root_dir
42
+ end
43
+
44
+ protected
45
+
46
+ def config
47
+ @config ||= Bridgetown.configuration({ root_dir: Dir.pwd })
48
+ end
49
+
50
+ def package_json
51
+ @package_json ||= begin
52
+ package_json_file = File.read(Bridgetown.sanitized_path(config.root_dir, "package.json"))
53
+ JSON.parse(package_json_file)
54
+ end
55
+ end
56
+
57
+ def perform(action)
58
+ automation = find_in_source_paths("#{action}.rb")
59
+ inside(New.created_site_dir || Dir.pwd) do
60
+ apply automation, verbose: false
61
+ end
62
+ end
63
+
64
+ def show_actions
65
+ say "Available actions:\n".bold
66
+
67
+ longest_action = supported_actions.keys.max_by(&:size).size
68
+ supported_actions.each do |action, description|
69
+ say "#{action.ljust(longest_action).to_s.bold.blue}\t# #{description}"
70
+ end
71
+ end
72
+
73
+ def supported_actions
74
+ {
75
+ setup: "Sets up an esbuild integration with Bridgetown in your project",
76
+ update: "Updates the Bridgetown esbuild defaults to the latest available version",
77
+ "migrate-from-webpack":
78
+ "Removes Webpack from your project and installs/configures esbuild",
79
+ }.with_indifferent_access
80
+ end
81
+ end
82
+ end
83
+ end