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
24 changes: 21 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,39 @@ jobs:
matrix:
ruby: ["3.3", "3.4", "4.0"]
rails: ["7.2", "8.0", "8.1"]
# `ember` is the `ember-cli/ember-new-output` tag used for the
# dummy app. `>= 6.8` uses the Vite-based blueprint, older
# versions use the classic Broccoli-based build.
ember: ["7.0.0"]
include:
- ruby: "4.0"
rails: "main"
# Disabled: rspec-rails 6.x (the newest the gemspec allows)
# fails to load specs against rails/rails main with
# `FrozenError: can't modify frozen Hash`. Re-enable once the
# rspec-rails dependency can be upgraded to a series that
# supports Rails main.
# - ruby: "4.0"
# rails: "main"
# ember: "7.0.0"
# Classic (Broccoli-based) blueprint coverage
- ruby: "3.4"
rails: "8.1"
ember: "5.12.0"
- ruby: "3.4"
rails: "8.1"
ember: "6.7.2"

env:
RAILS_ENV: "test"
RAILS_VERSION: "${{ matrix.rails }}"
EMBER_VERSION: "${{ matrix.ember }}"

steps:
- uses: "actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1" # v7.0.1

- name: "Install NodeJS"
uses: "actions/setup-node@820762786026740c76f36085b0efc47a31fe5020" # v7.0.0
with:
node-version: "20.x"
node-version: "22.x"

- name: "Install Ruby ${{ matrix.ruby }}"
uses: "ruby/setup-ruby@003a5c4d8d6321bd302e38f6f0ec593f77f06600" # v1.319.0
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
main
------

* Support Ember applications generated with the Vite-based blueprint
(`ember-cli >= 6.8`), without `ember-cli-rails-addon`
* Require `ember-cli-rails-assets >= 0.8.0`, which adds Vite support to
`include_ember_script_tags`

0.12.3
------

Expand Down
40 changes: 38 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ EmberCli.configure do |c|
end
```

Next, install the [ember-cli-rails-addon][addon]:
Next, if your Ember application uses the classic (Broccoli-based) build
(generated with `ember-cli < 6.8`), install the [ember-cli-rails-addon][addon]:

```bash
$ cd path/to/frontend
Expand All @@ -111,6 +112,10 @@ For instance, if you're using the `0.6.x` version of the gem, specify
}
```

If your Ember application uses the Vite-based build (generated with
`ember-cli >= 6.8`), do **not** install the addon: it is incompatible with the
Vite-based build, and `ember-cli-rails` does not require it there.

[addon]: https://github.com/rondale-sc/ember-cli-rails-addon/
[semver]: http://semver.org/

Expand Down Expand Up @@ -171,6 +176,17 @@ suites, configure the `default` task to depend on both `spec` and `ember:test`.
task default: [:spec, "ember:test"]
```

**Vite-based applications**

When Rails is running in development mode, classic (Broccoli-based) Ember
applications are built with `ember build --watch`, so changes to the Ember
application are picked up automatically.

Vite-based Ember applications (generated with `ember-cli >= 6.8`) are instead
built once, synchronously, when they are first requested. To pick up changes
to the Ember application, restart the Rails server, or iterate on the Ember
application directly with its own development server (`npm start`).

## Deploy

When Rails is running in production mode, EmberCLI-Rails stops doing runtime
Expand Down Expand Up @@ -449,6 +465,13 @@ actively supported method of serving EmberCLI applications.
However, for the sake of backwards compatibility, `ember-cli-rails` supports
injecting the EmberCLI-generated assets into an existing Rails layout.

**Note:** for Vite-based applications (generated with `ember-cli >= 6.8`),
use `include_ember_script_tags` on its own. It emits everything the
application needs to boot — the configuration `<meta>` tag, the stylesheet
and `modulepreload` links, and the ES module script tags — extracted from
the generated `index.html`. `include_ember_stylesheet_tags` only supports
classic (Broccoli-based) applications.

Following the example above, configure the mounted EmberCLI application to be
served by a custom controller (`ApplicationController`, in this case).

Expand Down Expand Up @@ -654,7 +677,20 @@ if (environment === 'development') {

This project supports:

* EmberCLI versions `>= 1.13.13`
* EmberCLI versions `>= 1.13.13` using the classic (Broccoli-based) build,
together with [ember-cli-rails-addon][addon]
* EmberCLI versions `>= 6.8` using the Vite-based build, without the addon

The test suite currently exercises the `5.12.x` and `6.7.x` (classic) and
`7.0.x` (Vite) series.

Note the following limitations for Vite-based applications:

* `include_ember_script_tags` emits the full set of tags the application
needs to boot, stylesheets included; `include_ember_stylesheet_tags` is
classic-only and must not be called for Vite-based applications
* in development, the application is built synchronously on first request
instead of being rebuilt on file changes

## Ruby and Rails support

Expand Down
26 changes: 26 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
# EmberCLI support

`ember-cli >= 6.8` generates applications that are built with [Vite] instead
of the classic Broccoli-based pipeline. `ember-cli-rails` supports both build
systems, and detects the Vite-based build by the presence of a `vite.config.*`
file in the Ember application's root.

When upgrading an Ember application to the Vite-based blueprint, note the
following differences in how `ember-cli-rails` treats it:

* Remove `ember-cli-rails-addon` from the application's `package.json`. The
addon is incompatible with the Vite-based build (it forces
`storeConfigInMeta` off and ships an initializer that imports the removed
`ember` module), and `ember-cli-rails` no longer needs it there.
* Use `include_ember_script_tags` on its own instead of pairing it with
`include_ember_stylesheet_tags`: for Vite-based applications it emits the
configuration meta tag, the stylesheet links, and the module script tags
all together, while `include_ember_stylesheet_tags` supports only classic
applications. This requires `ember-cli-rails-assets >= 0.8.0`.
* In development, the application is built synchronously on first request
instead of being rebuilt on file changes. Restart the Rails server to pick
up changes, or iterate with the Ember application's own development server
(`npm start`).

[Vite]: https://vitejs.dev

# Ruby support

According to [these release notes][latest-eol], Ruby versions prior to `2.5.x`
Expand Down
4 changes: 0 additions & 4 deletions bin/setup
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ bundle check || bundle install
# Add binstubs to PATH via export PATH=".git/safe/../../bin:$PATH" in ~/.zshenv
mkdir -p .git/safe

if ! command -v bower > /dev/null; then
npm install -g bower
fi

bin/setup_ember spec/dummy/my-app

echo '-- Install Ember dependencies'
Expand Down
35 changes: 27 additions & 8 deletions bin/setup_ember
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,47 @@

set -e

# EMBER_VERSION selects the `ember-cli/ember-new-output` tag to clone.
# Versions >= 6.8 use the Vite-based app blueprint, older versions use the
# classic Broccoli-based build.
EMBER_VERSION="${EMBER_VERSION:-7.0.0}"

setup_ember() {
local target="${1-spec/dummy/my-app}"

if ! [ -d $target ]; then
git clone -b 'v4.0.0' https://github.com/ember-cli/ember-new-output.git $target
git clone -b "v${EMBER_VERSION}" https://github.com/ember-cli/ember-new-output.git $target

echo '-- Make router catchall routes'
sed -i -e "s/'auto'/'hash'/" $target/config/environment.js
echo '-- Use hash location to serve the app from any mount point'
sed -i -e "s/locationType: '\(auto\|history\)'/locationType: 'hash'/" $target/config/environment.js

echo '-- Add an image to a template'
if [ -f "$target/app/templates/application.gjs" ]; then
# The Vite blueprint ships an `application.gjs`, which would conflict
# with the `application.hbs` fixture.
rm "$target/app/templates/application.gjs"
fi
cp spec/fixtures/application.hbs $target/app/templates/application.hbs

mkdir -p $target/public/assets
cp spec/fixtures/logo.png $target/public/assets

echo '-- Install ember-cli-rails-addon'
cd $target &&
npm install --save-dev ember-cli-rails-addon@rondale-sc/ember-cli-rails-addon
if [ -f "$target/vite.config.mjs" ]; then
# `ember-cli-rails-addon` is incompatible with the Vite-based
# blueprint, and `ember-cli-rails` does not require it there.
echo '-- Install NPM dependencies'
cd $target &&
npm install
else
echo '-- Install ember-cli-rails-addon'
cd $target &&
npm install --save-dev ember-cli-rails-addon@rondale-sc/ember-cli-rails-addon
fi

if [ -f "$target/bower.json" ]; then
if [ -f "bower.json" ]; then
echo '-- Install Bower dependencies'
cd $target && bower install
command -v bower > /dev/null || npm install -g bower
bower install
fi

echo '-- Successfully setup Ember'
Expand Down
2 changes: 1 addition & 1 deletion ember-cli-rails.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Gem::Specification.new do |spec|

spec.required_ruby_version = ">= 2.5.0"

spec.add_dependency "ember-cli-rails-assets", ">= 0.6.2", "< 1.0"
spec.add_dependency "ember-cli-rails-assets", ">= 0.8.0", "< 1.0"
spec.add_dependency "railties", ">= 4.2"
spec.add_dependency "rack", ">= 2.1", "< 4.0"
spec.add_dependency "terrapin", "~> 0.6.0"
Expand Down
9 changes: 8 additions & 1 deletion lib/ember_cli/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,14 @@ def compile
def build
unless EmberCli.skip?
if development?
build_and_watch
if paths.vite?
# The Vite-based blueprint (`ember-cli >= 6.8`) has no
# `ember-cli-rails-addon` to manage the build lock, so build
# synchronously instead of watching for changes.
compile
else
build_and_watch
end
elsif test?
compile
end
Expand Down
7 changes: 7 additions & 0 deletions lib/ember_cli/path_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ def bower_json
root.join("bower.json")
end

# Apps generated with the Vite-based blueprint (`ember-cli >= 6.8`)
# ship a Vite config file at their root.
def vite?
%w[vite.config.mjs vite.config.js vite.config.ts].
any? { |config| root.join(config).exist? }
end

def ember
@ember ||= begin
root.join("node_modules", "ember-cli", "bin", "ember").tap do |path|
Expand Down
6 changes: 5 additions & 1 deletion spec/dummy/app/views/pages/embedded.html.erb
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
<%= include_ember_script_tags "my-app" %>
<%= include_ember_stylesheet_tags "my-app" %>
<%# For Vite-based apps, `include_ember_script_tags` already emits the
stylesheet tags, and `include_ember_stylesheet_tags` is classic-only. %>
<% unless EmberCli["my-app"].paths.vite? %>
<%= include_ember_stylesheet_tags "my-app" %>
<% end %>
18 changes: 9 additions & 9 deletions spec/features/user_views_ember_app_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,27 @@
end

scenario "is redirected with trailing slash", js: false do
expect(embedded_path).to eq("/asset-helpers")
expect(include_index_path).to eq("/no-block")

visit embedded_path
visit include_index_path

expect(current_path).to eq("/asset-helpers/")
expect(current_path).to eq("/no-block/")
end

scenario "is redirected with trailing slash with query params", js: false do
expect(embedded_path(query: "foo")).to eq("/asset-helpers?query=foo")
expect(include_index_path(query: "foo")).to eq("/no-block?query=foo")

visit embedded_path(query: "foo")
visit include_index_path(query: "foo")

expect(page).to have_current_path("/asset-helpers/?query=foo")
expect(page).to have_current_path("/no-block/?query=foo")
end

scenario "is not redirected with trailing slash with params", js: false do
expect(embedded_path(query: "foo")).to eq("/asset-helpers?query=foo")
expect(include_index_path(query: "foo")).to eq("/no-block?query=foo")

visit "/asset-helpers/?query=foo"
visit "/no-block/?query=foo"

expect(page).to have_current_path("/asset-helpers/?query=foo")
expect(page).to have_current_path("/no-block/?query=foo")
end

def have_client_side_asset
Expand Down
24 changes: 24 additions & 0 deletions spec/lib/ember_cli/path_set_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,30 @@
end
end

describe "#vite?" do
it "is false when the app has no Vite config" do
path_set = build_path_set

expect(path_set).not_to be_vite
end

it "is true when the app has a vite.config.mjs" do
app = build_app
create_file(app_root_for(app).join("vite.config.mjs"))
path_set = build_path_set(app: app)

expect(path_set).to be_vite
end

it "is true when the app has a vite.config.js" do
app = build_app
create_file(app_root_for(app).join("vite.config.js"))
path_set = build_path_set(app: app)

expect(path_set).to be_vite
end
end

describe "#bower_components" do
it "is a child of #root" do
app = build_app(name: "foo")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
describe "GET assets/my-app.js" do
describe "GET a JavaScript asset" do
it "responds with the 'Cache-Control' header from Rails" do
build_ember_cli_assets

get "/assets/my-app.js"
get "/assets/#{javascript_asset_name}"

expect(headers["Cache-Control"]).to eq(cache_for_five_minutes)
end
Expand All @@ -11,6 +11,12 @@ def build_ember_cli_assets
EmberCli["my-app"].build
end

def javascript_asset_name
assets = EmberCli["my-app"].dist_path.join("assets")

Pathname.glob(assets.join("*.js")).first.basename
end

def cache_for_five_minutes
Dummy::Application::CACHE_CONTROL_FIVE_MINUTES
end
Expand Down
14 changes: 11 additions & 3 deletions spec/support/capybara.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
require "selenium/webdriver"

Capybara.register_driver :headless_chrome do |app|
options = Selenium::WebDriver::Chrome::Options.new(
args: %w[--no-sandbox --headless],
)

# Point Selenium at a specific Chrome binary, e.g. inside a container
# without a system-wide Chrome installation.
if ENV["CHROME_BIN"]
options.binary = ENV["CHROME_BIN"]
end

Capybara::Selenium::Driver.new(
app,
browser: :chrome,
options: Selenium::WebDriver::Chrome::Options.new(
args: %w[--no-sandbox --headless],
),
options: options,
)
end

Expand Down