From a04d95751e8923e640b623b69b03e1c6556b5335 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 17:11:00 +0000 Subject: [PATCH] Build a Vite application with vite build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `Command#build` drove every build through `ember build`. In a Vite-based project that command is on its way out: `ember build --help` there describes itself as a "Vestigial command in Vite-based projects. Use the `build` script from package.json instead", and has dropped every option but `--environment`, `--suppress-sizes` and `--output-path`. It refuses `--watch` and `--watcher` rather than forwarding them. Build such an application with `vite build` instead, which is what the blueprint's own `build` script runs. `Command#build` picks the command from the blueprint, and the two builders sit side by side so that what each blueprint needs stays visible. The flags follow from what the gem needs of a build: * `--mode` carries what `--environment` carried, from the same `build_environment` * `--outDir` carries what `--output-path` carried, the directory Rails serves the application from * `--emptyOutDir` keeps the directory cleared between builds. It lies outside the Ember application, where Vite leaves stale files in place unless asked * `--logLevel error` stands in for `--silent`, quieting the progress output while leaving errors visible Running `vite build` directly rather than the `build` script through a package manager is deliberate: the gem has to pass `--outDir`, and forwarding flags to a script is not portable — npm needs `--` before them, while pnpm passes that `--` on to the script, where the flags arrive as literal arguments and the build silently writes to `dist` instead. Co-Authored-By: Claude Opus 5 --- CHANGELOG.md | 5 +++ README.md | 4 ++- lib/ember_cli/command.rb | 53 +++++++++++++++++++++++++----- spec/lib/ember_cli/command_spec.rb | 32 +++++++++++++++++- 4 files changed, 83 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad812509..2782670c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ main ------ +* Build a Vite-based application with `vite build`, the command its own + `build` script runs, rather than with `ember build`. `ember build --help` + calls itself a "Vestigial command in Vite-based projects" and points at + that script. The output is unchanged; `silent` now quiets such a build + with `--logLevel error` rather than `ember build --silent` * Recognise an application as Vite-based from any of the six names Vite resolves its configuration from, rather than only `vite.config.js`, `vite.config.mjs` and `vite.config.ts`. An application configured in diff --git a/README.md b/README.md index 650fabba..f460fd47 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,9 @@ c.app :frontend, path: "~/projects/my-ember-app" - `path` - the path where your Ember CLI application is located. The default value is the name of your app in the Rails root. -- `silent` - this provides `--silent` option for Ember CLI commands to control verbosity of their output. +- `silent` - quiets the build. A classic application is built with + `ember build --silent`; a Vite-based one with `vite build --logLevel error`, + which keeps errors while dropping the progress output. - `package_manager` - the package manager that installs the application's NodeJS dependencies: `:npm` (the default), `:yarn`, or `:pnpm`. Name the diff --git a/lib/ember_cli/command.rb b/lib/ember_cli/command.rb index bea5925f..b79f00fc 100644 --- a/lib/ember_cli/command.rb +++ b/lib/ember_cli/command.rb @@ -14,7 +14,11 @@ def test end def build(watch: false) - ember_build(watch: watch) + if paths.vite? + vite_build + else + ember_build(watch: watch) + end end # Boots Vite's development server for an application generated with the @@ -43,6 +47,45 @@ def silent? options.fetch(:silent) { false } end + def build_environment + if EmberCli.env == "production" + "production" + else + "development" + end + end + + # The Vite-based blueprint (`ember-cli >= 6.8`) + # + # Builds the application the way its own `build` script does. + # `ember build` still drives such a build today, but `ember build --help` + # calls it a "Vestigial command in Vite-based projects" and has dropped + # every option but `--environment`, `--suppress-sizes` and + # `--output-path`. + # + # `--emptyOutDir` is needed because the output directory is outside the + # Ember application, where Vite leaves stale files in place unless asked + # to clear them. + def vite_build + line = Terrapin::CommandLine.new(paths.vite, [ + "build", + "--mode :mode", + "--outDir :output_path", + "--emptyOutDir", + ("--logLevel error" if silent?), + ].compact.join(" ")) + + line.command( + mode: build_environment, + output_path: paths.dist, + ) + end + + # The classic blueprint + # + # Builds the application with `ember build`, which watches for changes + # when asked. A Vite-based project has no equivalent: `ember build` there + # refuses `--watch`, and its development server watches instead. def ember_build(watch: false) line = Terrapin::CommandLine.new(paths.ember, [ "build", @@ -59,13 +102,5 @@ def ember_build(watch: false) watcher: process_watcher, ) end - - def build_environment - if EmberCli.env == "production" - "production" - else - "development" - end - end end end diff --git a/spec/lib/ember_cli/command_spec.rb b/spec/lib/ember_cli/command_spec.rb index 4a786a09..932c1f81 100644 --- a/spec/lib/ember_cli/command_spec.rb +++ b/spec/lib/ember_cli/command_spec.rb @@ -106,8 +106,38 @@ end end + describe "#build for a Vite-based application" do + it "builds a `vite build` command writing to the output path" do + paths = build_paths(vite?: true, vite: "path/to/vite", dist: "path/to/dist") + command = build_command(paths: paths) + + expect(command.build).to match(%r{path/to/vite build}) + expect(command.build).to match(/--mode 'development'/) + expect(command.build).to match(%r{--outDir 'path/to/dist'}) + expect(command.build).to match(/--emptyOutDir/) + end + + it "does not pass the flags `ember build` takes" do + paths = build_paths(vite?: true, vite: "path/to/vite") + command = build_command(paths: paths, options: { watcher: "events" }) + + expect(command.build(watch: true)).not_to match(/--watch/) + expect(command.build(watch: true)).not_to match(/--environment/) + end + + it "quiets the build when configured to be silent" do + paths = build_paths(vite?: true, vite: "path/to/vite") + + expect(build_command(paths: paths).build).not_to match(/--logLevel/) + + command = build_command(paths: paths, options: { silent: true }) + + expect(command.build).to match(/--logLevel error/) + end + end + def build_paths(**options) - double(options).as_null_object + double({ vite?: false }.merge(options)).as_null_object end def build_command(**options)