shopify_theme_builder 0.3.0 → 1.0.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 307e28c3c750b87460a048c082ba3c3287a480419c1b1c52faa84660de8b543a
4
- data.tar.gz: 1ea87aa101f2858f46090e8f12d61c231bb4671dbd10aaa53b5427497690ae28
3
+ metadata.gz: 7d2c29efc227a315501fc0e21d47b8cb268f8cbd7da2887754321e4ae02914b2
4
+ data.tar.gz: 51abf4c9808331d461b486325b2a29dc3804d2546befab0ab523530370f471d3
5
5
  SHA512:
6
- metadata.gz: 4997036e87e815574d49b2857e4c8f859fcd53afcd279652ec3b81b563d454bd0826c21101a616c82cb8b0b570817e1c3d6d20a827c7d91d0940f81ed5d4766c
7
- data.tar.gz: 199222a677430c7ec42719ef04574050327f61f8648c5096b7d60c9f06ac9a4c03b42fdbc90e307f41b46fd916eebb79fbac437a82c67ba00f87ce3805126279
6
+ metadata.gz: 06e8ea516c5e0fa0ea36769a21d7c4cfcfbd5c5cbd4c4cf1c62c60707e041e645e00a7e45fcbcdea0fb993f1eef34008682901934a9f6a62299084394ba252d0
7
+ data.tar.gz: 1df921934a3ba4cca6f783a4409e4480cba8adaba7363503a6fbec0faf528007f86e8a02a52ccf039f3992942f6766c0f8fd573ea5a81e6625e4d939fd3f3dd6
data/README.md CHANGED
@@ -33,6 +33,11 @@ _components/
33
33
  schema.json
34
34
  style.css
35
35
  index.js
36
+ controllers.js
37
+ assets/
38
+ tailwind-output.css
39
+ tailwind.css
40
+ controllers.js
36
41
  blocks/
37
42
  button.liquid
38
43
  ```
@@ -43,6 +48,34 @@ All files inside the `button` folder will be compiled into a single `button.liqu
43
48
 
44
49
  ShopifyThemeBuilder also supports Tailwind CSS integration. You can specify an input CSS file that includes Tailwind directives and an output CSS file where the compiled styles will be saved. The watcher will automatically run the Tailwind build process whenever changes are detected in the components folder.
45
50
 
51
+ ## Stimulus JS Support
52
+
53
+ ShopifyThemeBuilder supports Stimulus JS integration. You can specify an output JavaScript file where the compiled Stimulus controllers will be saved. The watcher will automatically compile the Stimulus controllers whenever changes are detected in the components folder.
54
+
55
+ ### How to create Stimulus controllers
56
+
57
+ To create Stimulus controllers, create a `controller.js` file inside your component folder. For example:
58
+
59
+ ```
60
+ _components/
61
+ my_component/
62
+ controller.js
63
+ ```
64
+
65
+ The content of the `controller.js` file should follow the Stimulus controller structure. For example:
66
+
67
+ ```javascript
68
+ class HelloController extends Controller {
69
+ connect() {
70
+ console.log("My component controller connected")
71
+ }
72
+ }
73
+
74
+ Stimulus.register("hello", HelloController)
75
+ ```
76
+
77
+ When the watcher runs, it will compile all `controller.js` files from your components into a single JavaScript file that can be included in your Shopify theme.
78
+
46
79
  ## Installation
47
80
 
48
81
  Install the gem and add to the application's Gemfile by executing:
@@ -51,6 +84,14 @@ Install the gem and add to the application's Gemfile by executing:
51
84
  bundle add shopify_theme_builder --group "development"
52
85
  ```
53
86
 
87
+ Run the gem's install command:
88
+
89
+ ```bash
90
+ bundle exec theme-builder install
91
+ ```
92
+
93
+ This will inject Tailwind CSS and Stimulus JS into your Shopify theme layout, and add an entry in the `Procfile.dev` to run the watcher if present.
94
+
54
95
  ## Usage
55
96
 
56
97
  To watch for changes in the default components folder and build the theme, run:
@@ -61,9 +102,10 @@ bundle exec theme-builder watch
61
102
 
62
103
  You can customize multiple options when running the watcher:
63
104
  - `--folders`: Specify one or more folders to watch (default is `_components`).
64
- - `--tailwind-input-file`: Specify the Tailwind CSS input file (default is `src/styles/tailwind.css`).
65
- - `--tailwind-output-file`: Specify the Tailwind CSS output file (default is `assets/tailwind-output.css`).
105
+ - `--tailwind-input-file`: Specify the Tailwind CSS input file (default is `./assets/tailwind.css`).
106
+ - `--tailwind-output-file`: Specify the Tailwind CSS output file (default is `./assets/tailwind-output.css`).
66
107
  - `--skip-tailwind`: Skip the Tailwind CSS build process (default is `false`).
108
+ - `--stimulus-output-file`: Specify the Stimulus JS output file (default is `./assets/controllers.js`).
67
109
 
68
110
  If you need help with all available options, or how to set them, run:
69
111
 
@@ -71,14 +113,24 @@ If you need help with all available options, or how to set them, run:
71
113
  bundle exec theme-builder help watch
72
114
  ```
73
115
 
74
- ## After Running the Watcher
116
+ ### Component generator
75
117
 
76
- The watcher will create a CSS file that can be included in your Shopify theme layout in this way:
118
+ This gem also provides a command to generate a new component with all the necessary files. To create a new component, run:
77
119
 
78
- ```liquid
79
- {{ 'tailwind-output.css' | asset_url | stylesheet_tag }}
120
+ ```bash
121
+ bundle exec theme-builder generate
80
122
  ```
81
123
 
124
+ You can customize the component type, name and folder by providing additional options:
125
+ - `--type`: Specify the component type (`section`, `block`, or `snippet`).
126
+ - `--name`: Specify the component name.
127
+ - `--folder`: Specify the components folder (default is `_components`).
128
+
129
+ ## Tested With
130
+
131
+ - [Skeleton Theme](https://github.com/Shopify/skeleton-theme)
132
+ - [Horizon Theme](https://github.com/Shopify/horizon)
133
+
82
134
  ## Development
83
135
 
84
136
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -88,9 +140,10 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
88
140
  ## Next Steps
89
141
 
90
142
  - [x] Run the tailwind build process automatically.
91
- - [ ] Add Stimulus JS support.
92
- - [ ] Create a command to build an example component with all the files.
93
- - [ ] Decompile existing Shopify files into components structure (?).
143
+ - [x] Add Stimulus JS support.
144
+ - [x] Create a command to build an example component with all the files.
145
+ - [ ] Investigate if it's possible to use import maps instead of a single file for Stimulus controllers.
146
+ - [ ] Decompile existing Shopify files into components structure (Is it really needed?).
94
147
 
95
148
  ## Contributing
96
149
 
@@ -0,0 +1,3 @@
1
+ <div data-controller="<%= name.parameterize %>">
2
+ <h1><%= name %> <%= type %></h1>
3
+ </div>
@@ -0,0 +1 @@
1
+ this is a comment
@@ -0,0 +1,5 @@
1
+ Stimulus.register("<%= name.parameterize %>", class extends Controller {
2
+ connect() {
3
+ console.log("Hello, Stimulus!", this.element)
4
+ }
5
+ })
@@ -0,0 +1,7 @@
1
+ <%= name %> snippet
2
+
3
+ @param {string} foo - A string value.
4
+ @param {string} [bar] - An optional string value.
5
+
6
+ @example
7
+ {% render '<%= name.parameterize %>', foo: 'Hello', bar: 'World' %}
@@ -0,0 +1 @@
1
+ console.log("Hello from the <%= name %> <%= type %>")
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "<%= name %>",
3
+ "tag": "<%= type == "section" ? "section" : "div" %>",
4
+ "class": "<%= name.parameterize %>",
5
+ "limit": 1,
6
+ "settings": [
7
+ {
8
+ "type": "text",
9
+ "id": "title",
10
+ "label": "Title"
11
+ }
12
+ ],
13
+ "max_blocks": 5,
14
+ "blocks": [{ "type": "@theme" }, { "type": "@app" }],
15
+ "presets": [
16
+ {
17
+ "name": "<%= name %>",
18
+ "settings": {
19
+ "title": "<%= name %>"
20
+ },
21
+ "blocks": []
22
+ }
23
+ ],
24
+ "locales": {
25
+ "en": {
26
+ "title": "<%= name %>"
27
+ }
28
+ },
29
+ "enabled_on": {
30
+ "templates": ["*"],
31
+ "groups": ["*"]
32
+ }
33
+ }
@@ -0,0 +1,3 @@
1
+ .<%= name.parameterize %> {
2
+ background-color: red;
3
+ }
@@ -12,8 +12,8 @@ module ShopifyThemeBuilder
12
12
  def build
13
13
  puts "Processing #{@files_to_process.count} files..."
14
14
 
15
- @files_to_process.each do |file|
16
- @processed_files << ShopifyThemeBuilder::LiquidProcessor.new(file).process
15
+ @files_to_process.each do |file, event|
16
+ @processed_files << ShopifyThemeBuilder::LiquidProcessor.new(file:, event:).process
17
17
  end
18
18
 
19
19
  puts "Built #{@processed_files.count} files." if @processed_files.any?
@@ -1,23 +1,194 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/string/inflections"
3
4
  require "thor"
4
5
 
5
6
  module ShopifyThemeBuilder
6
7
  # CommandLine class to handle CLI commands using Thor.
7
8
  class CommandLine < Thor
9
+ include Thor::Actions
10
+
11
+ SUPPORTED_TYPES = %w[section block snippet].freeze
12
+
13
+ attr_reader :type, :name
14
+
15
+ def self.source_root
16
+ File.expand_path("../..", __dir__)
17
+ end
18
+
8
19
  desc "watch", "Watch for changes in the components folder and build the theme accordingly"
9
20
  method_option :folders, type: :array, default: ["_components"], desc: "Folders to watch for changes"
10
21
  method_option :tailwind_input_file, type: :string, default: "./assets/tailwind.css", desc: "Tailwind CSS input file"
11
22
  method_option :tailwind_output_file, type: :string, default: "./assets/tailwind-output.css",
12
23
  desc: "Tailwind CSS output file"
13
24
  method_option :skip_tailwind, type: :boolean, default: false, desc: "Skip Tailwind CSS processing"
25
+ method_option :stimulus_output_file, type: :string, default: "./assets/controllers.js",
26
+ desc: "Stimulus controllers output file"
14
27
  def watch
15
28
  ShopifyThemeBuilder.watch(
16
29
  folders_to_watch: options[:folders],
17
30
  tailwind_input_file: options[:tailwind_input_file],
18
31
  tailwind_output_file: options[:tailwind_output_file],
19
- skip_tailwind: options[:skip_tailwind]
32
+ skip_tailwind: options[:skip_tailwind],
33
+ stimulus_output_file: options[:stimulus_output_file]
20
34
  )
21
35
  end
36
+
37
+ desc "install", "Set up your Shopify theme with Tailwind CSS, Stimulus JS, and the file watcher"
38
+ def install
39
+ add_tailwind_to_theme
40
+ add_stimulus_to_theme
41
+ add_watcher_to_procfile
42
+ end
43
+
44
+ desc "generate", "Generate an example component structure"
45
+ method_option :type, type: :string, desc: "Type of component to generate ('section', 'block' or 'snippet')"
46
+ method_option :name, type: :string, desc: "Name of the component to generate"
47
+ method_option :folder, type: :string, desc: "Folder to generate the component in"
48
+ def generate
49
+ @type = options[:type]
50
+ @name = options[:name]
51
+ folder = options[:folder]
52
+
53
+ until ShopifyThemeBuilder::CommandLine::SUPPORTED_TYPES.include?(@type)
54
+ unless @type.nil?
55
+ say_error "Error: Unsupported type '#{@type}'.\
56
+ Supported types are: #{ShopifyThemeBuilder::CommandLine::SUPPORTED_TYPES.join(", ")}"
57
+ end
58
+
59
+ @type = ask(
60
+ "Enter component type (section, block or snippet):",
61
+ limited_to: ShopifyThemeBuilder::CommandLine::SUPPORTED_TYPES
62
+ )
63
+ end
64
+
65
+ @name = ask("Enter component name (E.g., Slideshow):") if @name.nil?
66
+
67
+ folder = ask("Enter folder to generate the component in:", default: "_components") if folder.nil?
68
+
69
+ directory "generators", "#{folder}/#{@name.parameterize(separator: "_")}", exclude_pattern:
70
+ end
71
+
72
+ private
73
+
74
+ def exclude_pattern
75
+ return "doc.txt" unless @type == "snippet"
76
+
77
+ "schema.json"
78
+ end
79
+
80
+ def add_tailwind_to_theme
81
+ theme_file_path =
82
+ if File.exist?("snippets/stylesheets.liquid")
83
+ "snippets/stylesheets.liquid"
84
+ elsif File.exist?("layout/theme.liquid")
85
+ "layout/theme.liquid"
86
+ end
87
+
88
+ unless theme_file_path
89
+ say_error "Error: Could not find a theme file to inject Tailwind CSS.", :red
90
+ return
91
+ end
92
+
93
+ theme_file = File.read(theme_file_path)
94
+
95
+ if theme_file.include?("tailwind-output.css")
96
+ say "Tailwind CSS already included in #{theme_file_path}. Skipping injection.", :blue
97
+ return
98
+ end
99
+
100
+ injection_tag = "{{ 'tailwind-output.css' | asset_url | stylesheet_tag }}"
101
+
102
+ if theme_file_path == "snippets/stylesheets.liquid"
103
+ add_tailwind_to_snippet(theme_file_path, theme_file, injection_tag)
104
+ else
105
+ add_tailwind_to_layout(theme_file_path, theme_file, injection_tag)
106
+ end
107
+ end
108
+
109
+ def add_tailwind_to_snippet(theme_file_path, theme_file, injection_tag)
110
+ File.write(theme_file_path, "#{theme_file.chomp}\n#{injection_tag}\n")
111
+ say "Injected Tailwind CSS tag into #{theme_file_path}.", :green
112
+ end
113
+
114
+ def add_tailwind_to_layout(theme_file_path, theme_file, injection_tag)
115
+ stylesheet_tag_regex =
116
+ /(\{\{\s*['"][^'"]+['"]\s*\|\s*asset_url\s*\|\s*stylesheet_tag(?:\s*:\s*((?!\}\}).)*)?\s*\}\})/
117
+ if theme_file.match?(stylesheet_tag_regex)
118
+ updated_content = theme_file.sub(
119
+ stylesheet_tag_regex,
120
+ "\\1\n#{injection_tag}"
121
+ )
122
+ File.write(theme_file_path, updated_content)
123
+ say "Injected Tailwind CSS tag into #{theme_file_path}.", :green
124
+ else
125
+ say_error "Error: Could not find a way to inject Tailwind CSS. Please manually add the CSS tag.",
126
+ :red
127
+ end
128
+ end
129
+
130
+ def add_stimulus_to_theme
131
+ theme_file_path =
132
+ if File.exist?("snippets/scripts.liquid")
133
+ "snippets/scripts.liquid"
134
+ elsif File.exist?("layout/theme.liquid")
135
+ "layout/theme.liquid"
136
+ end
137
+
138
+ unless theme_file_path
139
+ say_error "Error: Could not find a theme file to inject Stimulus JS.", :red
140
+ return
141
+ end
142
+
143
+ theme_file = File.read(theme_file_path)
144
+
145
+ if theme_file.include?("controllers.js")
146
+ say "Stimulus JS already included in #{theme_file_path}. Skipping injection.", :blue
147
+ return
148
+ end
149
+
150
+ injection_tag = "<script type=\"module\" defer=\"defer\" src=\"{{ 'controllers.js' | asset_url }}\"></script>"
151
+
152
+ if theme_file_path == "snippets/scripts.liquid"
153
+ add_stimulus_to_snippet(theme_file_path, theme_file, injection_tag)
154
+ else
155
+ add_stimulus_to_layout(theme_file_path, theme_file, injection_tag)
156
+ end
157
+ end
158
+
159
+ def add_stimulus_to_snippet(theme_file_path, theme_file, injection_tag)
160
+ File.write(theme_file_path, "#{theme_file.chomp}\n#{injection_tag}\n")
161
+ say "Injected Stimulus JS tag into #{theme_file_path}.", :green
162
+ end
163
+
164
+ def add_stimulus_to_layout(theme_file_path, theme_file, injection_tag)
165
+ script_tag_regex = %r{(\s*)</body>}
166
+ if theme_file.match?(script_tag_regex)
167
+ updated_content = theme_file.sub(
168
+ script_tag_regex,
169
+ "\\1\n#{injection_tag}\n</body>"
170
+ )
171
+ File.write(theme_file_path, updated_content)
172
+ say "Injected Stimulus JS tag into #{theme_file_path}.", :green
173
+ else
174
+ say_error "Error: Could not find a way to inject Stimulus JS. Please manually add the JS tag.",
175
+ :red
176
+ end
177
+ end
178
+
179
+ def add_watcher_to_procfile
180
+ procfile_path = "Procfile.dev"
181
+ return unless File.exist?(procfile_path)
182
+
183
+ procfile_content = File.read(procfile_path)
184
+
185
+ if procfile_content.include?("theme-builder watch")
186
+ say "Watcher command already present in #{procfile_path}. Skipping addition.", :blue
187
+ return
188
+ end
189
+
190
+ File.write(procfile_path, "#{procfile_content.chomp}\ntheme-builder: bundle exec theme-builder watch\n")
191
+ say "Added watcher command to #{procfile_path}.", :green
192
+ end
22
193
  end
23
194
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "filewatcher"
4
+
5
+ module ShopifyThemeBuilder
6
+ # It wraps the Filewatcher functionality to monitor file changes.
7
+ # It delegates method calls to the underlying Filewatcher instance.
8
+ # Check: https://github.com/filewatcher/filewatcher
9
+ class Filewatcher
10
+ def initialize(...)
11
+ @filewatcher = ::Filewatcher.new(...)
12
+ end
13
+
14
+ def method_missing(name, ...)
15
+ if @filewatcher.respond_to?(name)
16
+ @filewatcher.send(name, ...)
17
+ else
18
+ super
19
+ end
20
+ end
21
+
22
+ def respond_to_missing?(name, include_private)
23
+ @filewatcher.respond_to?(name) || super
24
+ end
25
+ end
26
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "logger"
4
+
3
5
  module ShopifyThemeBuilder
4
6
  # LiquidProcessor is responsible for processing Liquid files
5
7
  # by combining various related files (Liquid, schema, CSS, JS, doc, comment)
@@ -22,17 +24,21 @@ module ShopifyThemeBuilder
22
24
  liquid: LIQUID_FILE_TYPES.map { |type| "#{type}.liquid" },
23
25
  schema: "schema.json",
24
26
  stylesheet: "style.css",
25
- javascript: "index.js"
27
+ javascript: "index.js",
28
+ stimulus: "controller.js"
26
29
  }.freeze
27
30
  SUPPORTED_VALUES = SUPPORTED_FILES.values.flatten.freeze
28
31
 
29
- def initialize(file)
30
- @file = file
32
+ def initialize(file:, event:)
33
+ @file = file.gsub("#{Dir.pwd}/", "")
34
+ @event = event
31
35
  @contents = +""
32
36
  @logger = Logger.new($stdout)
33
37
  end
34
38
 
35
39
  def process
40
+ return if component_deleted?
41
+
36
42
  return unless processable?
37
43
 
38
44
  compile_content
@@ -44,9 +50,28 @@ module ShopifyThemeBuilder
44
50
 
45
51
  private
46
52
 
53
+ # If a liquid file is deleted, we want to delete the compiled file as well,
54
+ # and skip further processing.
55
+ def component_deleted?
56
+ return false unless File.extname(@file) == ".liquid" && @event == :deleted
57
+
58
+ file_to_remove = compiled_filename(file_type: File.basename(@file, ".liquid"))
59
+
60
+ if File.exist?(file_to_remove)
61
+ File.delete(file_to_remove)
62
+ @logger.info "Deleted compiled file: #{file_to_remove}"
63
+ end
64
+
65
+ true
66
+ end
67
+
47
68
  # Returns true if the file is processable, false otherwise.
48
69
  def processable?
49
- supported? && correct_liquid_file? && correct_filename?
70
+ !directory? && supported? && correct_liquid_file? && correct_filename?
71
+ end
72
+
73
+ def directory?
74
+ File.directory?(@file)
50
75
  end
51
76
 
52
77
  # Checks if the file is in the list of supported files.
@@ -110,7 +135,7 @@ module ShopifyThemeBuilder
110
135
 
111
136
  # Returns the compiled filename based on the component name and liquid file type.
112
137
  # Example: _folder_to_watch/button/section.liquid -> sections/button.liquid
113
- def compiled_filename
138
+ def compiled_filename(file_type: liquid_file_type)
114
139
  filename_arr = file_dir.split(File::SEPARATOR) # Split the directory path into an array.
115
140
  filename_arr = filename_arr.drop(1) # Remove the base components folder from the path. E.g., _components
116
141
  filename_arr -= LIQUID_FILE_TYPES # Remove liquid file types from the path. E.g., section, snippet, block
@@ -119,12 +144,12 @@ module ShopifyThemeBuilder
119
144
 
120
145
  return nil if filename.empty?
121
146
 
122
- "#{liquid_file_type}s/#{filename}.liquid"
147
+ "#{file_type}s/#{filename}.liquid"
123
148
  end
124
149
 
125
150
  # Compiles the content by aggregating various related files.
126
151
  def compile_content
127
- SUPPORTED_FILES.each_key do |key|
152
+ SUPPORTED_FILES.except(:stimulus).each_key do |key|
128
153
  @contents << formatted_content(key)
129
154
  end
130
155
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ShopifyThemeBuilder
4
- VERSION = "0.3.0"
4
+ VERSION = "1.0.0"
5
5
  end
@@ -1,27 +1,128 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "filewatcher"
3
+ require "fileutils"
4
+ require "tailwindcss/ruby"
4
5
 
5
6
  module ShopifyThemeBuilder
6
- # Watcher class for ShopifyThemeBuilder.
7
- # It wraps the Filewatcher functionality to monitor file changes.
8
- # It delegates method calls to the underlying Filewatcher instance.
9
- # Check: https://github.com/filewatcher/filewatcher
7
+ # Watcher is responsible for monitoring specified folders for changes
8
+ # and triggering the build process for Shopify theme components.
10
9
  class Watcher
11
- def initialize(...)
12
- @filewatcher = Filewatcher.new(...)
10
+ def initialize(
11
+ folders_to_watch: ["_components"],
12
+ tailwind_input_file: "./assets/tailwind.css",
13
+ tailwind_output_file: "./assets/tailwind-output.css",
14
+ skip_tailwind: false,
15
+ stimulus_output_file: "./assets/controllers.js"
16
+ )
17
+ @folders_to_watch = folders_to_watch
18
+ @tailwind_input_file = tailwind_input_file
19
+ @tailwind_output_file = tailwind_output_file
20
+ @skip_tailwind = skip_tailwind
21
+ @stimulus_output_file = stimulus_output_file
22
+ @stimulus_controller_file = ShopifyThemeBuilder::LiquidProcessor::SUPPORTED_FILES[:stimulus]
13
23
  end
14
24
 
15
- def method_missing(name, ...)
16
- if @filewatcher.respond_to?(name)
17
- @filewatcher.send(name, ...)
18
- else
19
- super
25
+ def watch
26
+ create_folders
27
+
28
+ initial_build
29
+
30
+ create_tailwind_file
31
+
32
+ run_tailwind
33
+
34
+ run_stimulus
35
+
36
+ watch_folders
37
+ end
38
+
39
+ private
40
+
41
+ def create_folders
42
+ puts "Creating necessary folders..."
43
+
44
+ FileUtils.mkdir_p(@folders_to_watch)
45
+ FileUtils.mkdir_p("sections")
46
+ FileUtils.mkdir_p("blocks")
47
+ FileUtils.mkdir_p("snippets")
48
+ end
49
+
50
+ def initial_build
51
+ puts "Doing an initial build..."
52
+
53
+ files_to_process = {}
54
+
55
+ @folders_to_watch.each do |folder|
56
+ Dir.glob("#{folder}/**/*.*").each do |file|
57
+ files_to_process[file] = :updated
58
+ end
59
+ end
60
+
61
+ Builder.new(files_to_process: files_to_process).build
62
+ end
63
+
64
+ def watch_folders
65
+ puts "Watching for changes in '#{@folders_to_watch.join(", ")}' folders..."
66
+
67
+ Filewatcher.new(@folders_to_watch).watch do |changes|
68
+ Builder.new(files_to_process: changes).build
69
+
70
+ run_tailwind
71
+
72
+ run_stimulus if changes.keys.any? { |f| f.end_with?(@stimulus_controller_file) }
20
73
  end
21
74
  end
22
75
 
23
- def respond_to_missing?(name, include_private)
24
- @filewatcher.respond_to?(name) || super
76
+ def run_tailwind
77
+ return if @skip_tailwind
78
+
79
+ puts "Running Tailwind CSS build..."
80
+
81
+ system("tailwindcss", "-i", @tailwind_input_file, "-o", @tailwind_output_file)
82
+ end
83
+
84
+ def create_tailwind_file
85
+ return if @skip_tailwind || File.exist?(@tailwind_input_file)
86
+
87
+ puts "Creating default Tailwind CSS input file at '#{@tailwind_input_file}'..."
88
+
89
+ FileUtils.mkdir_p(File.dirname(@tailwind_input_file))
90
+ File.write @tailwind_input_file, tailwind_base_config
91
+ end
92
+
93
+ def tailwind_base_config
94
+ return '@import "tailwindcss";' if Gem::Version.new(Tailwindcss::Ruby::VERSION) >= Gem::Version.new("4.0.0")
95
+
96
+ system("tailwindcss", "init") unless File.exist?("tailwind.config.js")
97
+
98
+ <<~TAILWIND.strip
99
+ @tailwind base;
100
+ @tailwind components;
101
+ @tailwind utilities;
102
+ TAILWIND
103
+ end
104
+
105
+ def run_stimulus
106
+ controllers_files = @folders_to_watch.map { Dir.glob("#{_1}/**/#{@stimulus_controller_file}") }.flatten
107
+
108
+ return if controllers_files.empty?
109
+
110
+ puts "Building Stimulus controllers..."
111
+
112
+ content = +base_stimulus_content
113
+
114
+ controllers_files.each do |file|
115
+ content << File.read(file)
116
+ content << "\n"
117
+ end
118
+
119
+ FileUtils.mkdir_p(File.dirname(@stimulus_output_file))
120
+ File.write(@stimulus_output_file, content.strip)
121
+ end
122
+
123
+ def base_stimulus_content
124
+ "import { Application, Controller } from \"https://unpkg.com/@hotwired/stimulus/dist/stimulus.js\"\n\
125
+ window.Stimulus = Application.start()\n\n"
25
126
  end
26
127
  end
27
128
  end
@@ -1,70 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "fileutils"
4
- require "logger"
5
3
  require_relative "shopify_theme_builder/version"
6
- require_relative "shopify_theme_builder/watcher"
4
+ require_relative "shopify_theme_builder/filewatcher"
7
5
  require_relative "shopify_theme_builder/liquid_processor"
8
6
  require_relative "shopify_theme_builder/builder"
9
7
  require_relative "shopify_theme_builder/command_line"
8
+ require_relative "shopify_theme_builder/watcher"
10
9
 
11
10
  # The main module for ShopifyThemeBuilder.
12
11
  module ShopifyThemeBuilder
13
- class << self
14
- def watch(
15
- folders_to_watch: ["_components"],
16
- tailwind_input_file: "./assets/tailwind.css",
17
- tailwind_output_file: "./assets/tailwind-output.css",
18
- skip_tailwind: false
19
- )
20
- create_folders(folders_to_watch)
21
-
22
- initial_build(folders_to_watch)
23
-
24
- run_tailwind(tailwind_input_file, tailwind_output_file) unless skip_tailwind
25
-
26
- watch_folders(folders_to_watch) do
27
- run_tailwind(tailwind_input_file, tailwind_output_file) unless skip_tailwind
28
- end
29
- end
30
-
31
- private
32
-
33
- def create_folders(folders_to_watch)
34
- puts "Creating necessary folders..."
35
-
36
- FileUtils.mkdir_p(folders_to_watch)
37
- FileUtils.mkdir_p("sections")
38
- FileUtils.mkdir_p("blocks")
39
- FileUtils.mkdir_p("snippets")
40
- end
41
-
42
- def initial_build(folders_to_watch)
43
- puts "Doing an initial build..."
44
-
45
- folders_to_watch.each do |folder|
46
- Builder.new(files_to_process: Dir.glob("#{folder}/**/*.*")).build
47
- end
48
- end
49
-
50
- def watch_folders(folders_to_watch)
51
- puts "Watching for changes in '#{folders_to_watch.join(", ")}' folders..."
52
-
53
- Watcher.new(folders_to_watch).watch do |changes|
54
- changes.each_key do |filename|
55
- relative_filename = filename.gsub("#{Dir.pwd}/", "")
56
-
57
- Builder.new(files_to_process: [relative_filename]).build if relative_filename.start_with?(*folders_to_watch)
58
- end
59
-
60
- yield if block_given?
61
- end
62
- end
63
-
64
- def run_tailwind(tailwind_input_file, tailwind_output_file)
65
- puts "Running Tailwind CSS build..."
66
-
67
- system("tailwindcss", "-i", tailwind_input_file, "-o", tailwind_output_file)
68
- end
12
+ def self.watch(...)
13
+ Watcher.new(...).watch
69
14
  end
70
15
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shopify_theme_builder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Massimiliano Lattanzio
@@ -11,33 +11,39 @@ cert_chain: []
11
11
  date: 1980-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: filewatcher
14
+ name: activesupport
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '2.1'
19
+ version: '6.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '9.0'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
25
28
  - !ruby/object:Gem::Version
26
- version: '2.1'
29
+ version: '6.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '9.0'
27
33
  - !ruby/object:Gem::Dependency
28
- name: logger
34
+ name: filewatcher
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
37
  - - "~>"
32
38
  - !ruby/object:Gem::Version
33
- version: '1.7'
39
+ version: '2.1'
34
40
  type: :runtime
35
41
  prerelease: false
36
42
  version_requirements: !ruby/object:Gem::Requirement
37
43
  requirements:
38
44
  - - "~>"
39
45
  - !ruby/object:Gem::Version
40
- version: '1.7'
46
+ version: '2.1'
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: tailwindcss-ruby
43
49
  requirement: !ruby/object:Gem::Requirement
@@ -84,9 +90,17 @@ files:
84
90
  - README.md
85
91
  - Rakefile
86
92
  - exe/theme-builder
93
+ - generators/%type%.liquid.tt
94
+ - generators/comment.txt
95
+ - generators/controller.js.tt
96
+ - generators/doc.txt.tt
97
+ - generators/index.js.tt
98
+ - generators/schema.json.tt
99
+ - generators/style.css.tt
87
100
  - lib/shopify_theme_builder.rb
88
101
  - lib/shopify_theme_builder/builder.rb
89
102
  - lib/shopify_theme_builder/command_line.rb
103
+ - lib/shopify_theme_builder/filewatcher.rb
90
104
  - lib/shopify_theme_builder/liquid_processor.rb
91
105
  - lib/shopify_theme_builder/version.rb
92
106
  - lib/shopify_theme_builder/watcher.rb
@@ -115,7 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
115
129
  - !ruby/object:Gem::Version
116
130
  version: '0'
117
131
  requirements: []
118
- rubygems_version: 3.7.2
132
+ rubygems_version: 3.6.9
119
133
  specification_version: 4
120
134
  summary: An opinionated builder for Shopify themes using nested folders, Tailwind
121
135
  CSS, and Stimulus.