Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ RSpec/NestedGroups:
Metrics/ClassLength:
Exclude:
- 'lib/shopify_theme_builder/liquid_processor.rb'
- 'lib/shopify_theme_builder/command_line.rb'

Metrics/MethodLength:
Exclude:
Expand Down
22 changes: 8 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ Install the gem and add to the application's Gemfile by executing:
bundle add shopify_theme_builder --group "development"
```

Run the gem's install command:

```bash
bundle exec theme-builder install
```

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.

## Usage

To watch for changes in the default components folder and build the theme, run:
Expand Down Expand Up @@ -118,20 +126,6 @@ You can customize the component type, name and folder by providing additional op
- `--name`: Specify the component name.
- `--folder`: Specify the components folder (default is `_components`).

## After Running the Watcher

The watcher will create a CSS file that can be included in your Shopify theme layout in this way:

```liquid
{{ 'tailwind-output.css' | asset_url | stylesheet_tag }}
```

And a JavaScript file that can be included in your Shopify theme layout in this way:

```liquid
<script type="module" src="{{ 'controllers.js' | asset_url }}"></script>
```

## Development

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.
Expand Down
121 changes: 121 additions & 0 deletions lib/shopify_theme_builder/command_line.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ def watch
)
end

desc "install", "Set up your Shopify theme with Tailwind CSS, Stimulus JS, and the file watcher"
def install
add_tailwind_to_theme
add_stimulus_to_theme
add_watcher_to_procfile
end

desc "generate", "Generate an example component structure"
method_option :type, type: :string, desc: "Type of component to generate ('section', 'block' or 'snippet')"
method_option :name, type: :string, desc: "Name of the component to generate"
Expand Down Expand Up @@ -69,5 +76,119 @@ def exclude_pattern

"schema.json"
end

def add_tailwind_to_theme
theme_file_path =
if File.exist?("snippets/stylesheets.liquid")
"snippets/stylesheets.liquid"
elsif File.exist?("layout/theme.liquid")
"layout/theme.liquid"
end

unless theme_file_path
say_error "Error: Could not find a theme file to inject Tailwind CSS.", :red
return
end

theme_file = File.read(theme_file_path)

if theme_file.include?("tailwind-output.css")
say "Tailwind CSS already included in #{theme_file_path}. Skipping injection.", :blue
return
end

injection_tag = "{{ 'tailwind-output.css' | asset_url | stylesheet_tag }}"

if theme_file_path == "snippets/stylesheets.liquid"
add_tailwind_to_snippet(theme_file_path, theme_file, injection_tag)
else
add_tailwind_to_layout(theme_file_path, theme_file, injection_tag)
end
end

def add_tailwind_to_snippet(theme_file_path, theme_file, injection_tag)
File.write(theme_file_path, "#{theme_file.chomp}\n#{injection_tag}\n")
say "Injected Tailwind CSS tag into #{theme_file_path}.", :green
end

def add_tailwind_to_layout(theme_file_path, theme_file, injection_tag)
stylesheet_tag_regex =
/(\{\{\s*['"][^'"]+['"]\s*\|\s*asset_url\s*\|\s*stylesheet_tag(?:\s*:\s*((?!\}\}).)*)?\s*\}\})/
if theme_file.match?(stylesheet_tag_regex)
updated_content = theme_file.sub(
stylesheet_tag_regex,
"\\1\n#{injection_tag}"
)
File.write(theme_file_path, updated_content)
say "Injected Tailwind CSS tag into #{theme_file_path}.", :green
else
say_error "Error: Could not find a way to inject Tailwind CSS. Please manually add the CSS tag.",
:red
end
end

def add_stimulus_to_theme
theme_file_path =
if File.exist?("snippets/scripts.liquid")
"snippets/scripts.liquid"
elsif File.exist?("layout/theme.liquid")
"layout/theme.liquid"
end

unless theme_file_path
say_error "Error: Could not find a theme file to inject Stimulus JS.", :red
return
end

theme_file = File.read(theme_file_path)

if theme_file.include?("controllers.js")
say "Stimulus JS already included in #{theme_file_path}. Skipping injection.", :blue
return
end

injection_tag = "<script type=\"module\" defer=\"defer\" src=\"{{ 'controllers.js' | asset_url }}\"></script>"

if theme_file_path == "snippets/scripts.liquid"
add_stimulus_to_snippet(theme_file_path, theme_file, injection_tag)
else
add_stimulus_to_layout(theme_file_path, theme_file, injection_tag)
end
end

def add_stimulus_to_snippet(theme_file_path, theme_file, injection_tag)
File.write(theme_file_path, "#{theme_file.chomp}\n#{injection_tag}\n")
say "Injected Stimulus JS tag into #{theme_file_path}.", :green
end
Comment on lines +109 to +162
Copy link

Copilot AI Jan 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The methods add_tailwind_to_snippet and add_stimulus_to_snippet contain duplicated logic. Consider extracting this into a shared helper method (e.g., add_tag_to_snippet) that accepts the file path, file content, and injection tag as parameters to reduce code duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it's not part of the library, but just a utility command, it's fine as it is.


def add_stimulus_to_layout(theme_file_path, theme_file, injection_tag)
script_tag_regex = %r{(\s*)</body>}
if theme_file.match?(script_tag_regex)
updated_content = theme_file.sub(
script_tag_regex,
"\\1\n#{injection_tag}\n</body>"
)
File.write(theme_file_path, updated_content)
say "Injected Stimulus JS tag into #{theme_file_path}.", :green
else
say_error "Error: Could not find a way to inject Stimulus JS. Please manually add the JS tag.",
:red
end
end

def add_watcher_to_procfile
procfile_path = "Procfile.dev"
return unless File.exist?(procfile_path)

procfile_content = File.read(procfile_path)

if procfile_content.include?("theme-builder watch")
say "Watcher command already present in #{procfile_path}. Skipping addition.", :blue
return
end

File.write(procfile_path, "#{procfile_content.chomp}\ntheme-builder: bundle exec theme-builder watch\n")
say "Added watcher command to #{procfile_path}.", :green
end
end
end
Loading