├── .devcontainer ├── Dockerfile ├── base.Dockerfile └── devcontainer.json ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Appraisals ├── Gemfile ├── Gemfile.lock ├── MIT-LICENSE ├── README.md ├── Rakefile ├── bin ├── release └── test ├── dartsass-rails.gemspec ├── exe └── dartsass ├── gemfiles ├── rails_7_0_propshaft.gemfile ├── rails_7_0_propshaft.gemfile.lock ├── rails_7_0_sprockets.gemfile ├── rails_7_0_sprockets.gemfile.lock ├── rails_7_1_propshaft.gemfile ├── rails_7_1_propshaft.gemfile.lock ├── rails_7_1_sprockets.gemfile ├── rails_7_1_sprockets.gemfile.lock ├── rails_main_propshaft.gemfile └── rails_main_sprockets.gemfile ├── lib ├── dartsass-rails.rb ├── dartsass │ ├── engine.rb │ ├── runner.rb │ └── version.rb ├── install │ ├── Procfile.dev │ ├── application.scss │ ├── dartsass.rb │ └── dev └── tasks │ ├── build.rake │ ├── clobber.rake │ └── install.rake └── test ├── installer_test.rb └── test_helper.rb /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # [Choice] Ruby version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.0, 2, 2.7, 2.6, 3-bullseye, 3.0-bullseye, 2-bullseye, 2.7-bullseye, 2.6-bullseye, 3-buster, 3.0-buster, 2-buster, 2.7-buster, 2.6-buster 2 | ARG VARIANT=2-bullseye 3 | FROM mcr.microsoft.com/vscode/devcontainers/ruby:0-${VARIANT} 4 | 5 | # [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 6 | ARG NODE_VERSION="none" 7 | RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi 8 | 9 | # [Optional] Uncomment this section to install additional OS packages. 10 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 11 | # && apt-get -y install --no-install-recommends 12 | 13 | # [Optional] Uncomment this line to install additional gems. 14 | # RUN gem install 15 | 16 | # [Optional] Uncomment this line to install global node packages. 17 | # RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 -------------------------------------------------------------------------------- /.devcontainer/base.Dockerfile: -------------------------------------------------------------------------------- 1 | # [Choice] Ruby version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.0, 2, 2.7, 2.6, 3-bullseye, 3.0-bullseye, 2-bullseye, 2.7-bullseye, 2.6-bullseye, 3-buster, 3.0-buster, 2-buster, 2.7-buster, 2.6-buster 2 | ARG VARIANT=2-bullseye 3 | FROM ruby:${VARIANT} 4 | 5 | # Copy library scripts to execute 6 | COPY library-scripts/*.sh library-scripts/*.env /tmp/library-scripts/ 7 | 8 | # [Option] Install zsh 9 | ARG INSTALL_ZSH="true" 10 | # [Option] Upgrade OS packages to their latest versions 11 | ARG UPGRADE_PACKAGES="true" 12 | # Install needed packages and setup non-root user. Use a separate RUN statement to add your own dependencies. 13 | ARG USERNAME=vscode 14 | ARG USER_UID=1000 15 | ARG USER_GID=$USER_UID 16 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 17 | # Remove imagemagick due to https://security-tracker.debian.org/tracker/CVE-2019-10131 18 | && apt-get purge -y imagemagick imagemagick-6-common \ 19 | # Install common packages, non-root user, rvm, core build tools 20 | && bash /tmp/library-scripts/common-debian.sh "${INSTALL_ZSH}" "${USERNAME}" "${USER_UID}" "${USER_GID}" "${UPGRADE_PACKAGES}" "true" "true" \ 21 | && bash /tmp/library-scripts/ruby-debian.sh "none" "${USERNAME}" "true" "true" \ 22 | && apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* 23 | 24 | # [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 25 | ARG NODE_VERSION="none" 26 | ENV NVM_DIR=/usr/local/share/nvm 27 | ENV NVM_SYMLINK_CURRENT=true \ 28 | PATH=${NVM_DIR}/current/bin:${PATH} 29 | RUN bash /tmp/library-scripts/node-debian.sh "${NVM_DIR}" "${NODE_VERSION}" "${USERNAME}" \ 30 | && apt-get clean -y && rm -rf /var/lib/apt/lists/* 31 | 32 | # Remove library scripts for final image 33 | RUN rm -rf /tmp/library-scripts 34 | 35 | # [Optional] Uncomment this section to install additional OS packages. 36 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 37 | # && apt-get -y install --no-install-recommends 38 | 39 | # [Optional] Uncomment this line to install additional gems. 40 | # RUN gem install 41 | 42 | # [Optional] Uncomment this line to install global node packages. 43 | # RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.205.2/containers/ruby 3 | { 4 | "name": "Ruby", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | "args": { 8 | // Update 'VARIANT' to pick a Ruby version: 3, 3.0, 2, 2.7, 2.6 9 | // Append -bullseye or -buster to pin to an OS version. 10 | // Use -bullseye variants on local on arm64/Apple Silicon. 11 | "VARIANT": "3-bullseye", 12 | // Options 13 | "NODE_VERSION": "lts/*" 14 | } 15 | }, 16 | 17 | // Set *default* container specific settings.json values on container create. 18 | "settings": {}, 19 | 20 | // Add the IDs of extensions you want installed when the container is created. 21 | "extensions": [ 22 | "rebornix.Ruby" 23 | ], 24 | 25 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 26 | // "forwardPorts": [], 27 | 28 | // Use 'postCreateCommand' to run commands after the container is created. 29 | // "postCreateCommand": "ruby --version", 30 | 31 | // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 32 | "remoteUser": "vscode", 33 | "features": { 34 | "github-cli": "latest" 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | jobs: 4 | tests: 5 | strategy: 6 | fail-fast: false 7 | matrix: 8 | ruby-version: 9 | - "3.0" 10 | - "3.1" 11 | - "3.2" 12 | - "3.3" 13 | gemfile: 14 | - Gemfile 15 | - gemfiles/rails_7_0_propshaft.gemfile 16 | - gemfiles/rails_7_1_propshaft.gemfile 17 | - gemfiles/rails_main_propshaft.gemfile 18 | - gemfiles/rails_7_0_sprockets.gemfile 19 | - gemfiles/rails_7_1_sprockets.gemfile 20 | - gemfiles/rails_main_sprockets.gemfile 21 | exclude: 22 | - ruby-version: "3.0" 23 | gemfile: gemfiles/rails_main_propshaft.gemfile 24 | - ruby-version: "3.0" 25 | gemfile: gemfiles/rails_main_sprockets.gemfile 26 | continue-on-error: [ false ] 27 | 28 | name: ${{ format('Tests (Ruby {0}, {1})', matrix.ruby-version, matrix.gemfile) }} 29 | runs-on: ubuntu-latest 30 | continue-on-error: ${{ matrix.continue-on-error }} 31 | 32 | env: 33 | BUNDLE_GEMFILE: ${{ matrix.gemfile }} 34 | 35 | steps: 36 | - uses: actions/checkout@v4 37 | 38 | - name: Remove Gemfile lock 39 | run: | 40 | rm -f $BUNDLE_GEMFILE.lock 41 | 42 | - name: Install Ruby 43 | uses: ruby/setup-ruby@v1 44 | with: 45 | ruby-version: ${{ matrix.ruby-version }} 46 | bundler-cache: true 47 | 48 | # This prevents a "Failed to build gem native extension" error when 49 | # running `bundle install` inside a Rails app that is generated for 50 | # testing. The error occurs only in CI, and only when using Ruby 3.0 and 51 | # Rails >= 7.1. 52 | - name: Install sass-embedded gem 53 | if: ${{ matrix.ruby-version == '3.0' }} 54 | run: gem install sass-embedded 55 | 56 | - name: Run tests 57 | run: | 58 | bundle exec rake 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /doc/ 3 | /log/*.log 4 | /pkg/ 5 | /tmp/ 6 | /test/dummy/db/*.sqlite3 7 | /test/dummy/db/*.sqlite3-* 8 | /test/dummy/log/*.log 9 | /test/dummy/storage/ 10 | /test/dummy/tmp/ 11 | /node_modules 12 | .byebug_history 13 | *.gem 14 | .idea/ 15 | **/tmp/ 16 | /exe/*/tailwindcss 17 | 18 | # Ignore Gemfile.lock files for Rails main branch. 19 | /gemfiles/rails_main*.gemfile.lock 20 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | appraise "rails_7_0_sprockets" do 2 | gem "rails", "~> 7.0.0" 3 | gem "sprockets-rails" 4 | end 5 | 6 | appraise "rails_7_0_propshaft" do 7 | gem "rails", "~> 7.0.0" 8 | gem "propshaft" 9 | end 10 | 11 | appraise "rails_7_1_sprockets" do 12 | gem "rails", "~> 7.1.0" 13 | gem "sprockets-rails" 14 | end 15 | 16 | appraise "rails_7_1_propshaft" do 17 | gem "rails", "~> 7.1.0" 18 | gem "propshaft" 19 | end 20 | 21 | appraise "rails_main_sprockets" do 22 | gem "rails", github: "rails/rails", branch: "main" 23 | gem "sprockets-rails" 24 | end 25 | 26 | appraise "rails_main_propshaft" do 27 | gem "rails", github: "rails/rails", branch: "main" 28 | gem "propshaft" 29 | end 30 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | gemspec 5 | 6 | gem "appraisal" 7 | gem "rails", "~> 6.1.0" 8 | gem "sqlite3" 9 | gem "debug", ">= 1.0.0" 10 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | dartsass-rails (0.5.1) 5 | railties (>= 6.0.0) 6 | sass-embedded (~> 1.63) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | actioncable (6.1.7.8) 12 | actionpack (= 6.1.7.8) 13 | activesupport (= 6.1.7.8) 14 | nio4r (~> 2.0) 15 | websocket-driver (>= 0.6.1) 16 | actionmailbox (6.1.7.8) 17 | actionpack (= 6.1.7.8) 18 | activejob (= 6.1.7.8) 19 | activerecord (= 6.1.7.8) 20 | activestorage (= 6.1.7.8) 21 | activesupport (= 6.1.7.8) 22 | mail (>= 2.7.1) 23 | actionmailer (6.1.7.8) 24 | actionpack (= 6.1.7.8) 25 | actionview (= 6.1.7.8) 26 | activejob (= 6.1.7.8) 27 | activesupport (= 6.1.7.8) 28 | mail (~> 2.5, >= 2.5.4) 29 | rails-dom-testing (~> 2.0) 30 | actionpack (6.1.7.8) 31 | actionview (= 6.1.7.8) 32 | activesupport (= 6.1.7.8) 33 | rack (~> 2.0, >= 2.0.9) 34 | rack-test (>= 0.6.3) 35 | rails-dom-testing (~> 2.0) 36 | rails-html-sanitizer (~> 1.0, >= 1.2.0) 37 | actiontext (6.1.7.8) 38 | actionpack (= 6.1.7.8) 39 | activerecord (= 6.1.7.8) 40 | activestorage (= 6.1.7.8) 41 | activesupport (= 6.1.7.8) 42 | nokogiri (>= 1.8.5) 43 | actionview (6.1.7.8) 44 | activesupport (= 6.1.7.8) 45 | builder (~> 3.1) 46 | erubi (~> 1.4) 47 | rails-dom-testing (~> 2.0) 48 | rails-html-sanitizer (~> 1.1, >= 1.2.0) 49 | activejob (6.1.7.8) 50 | activesupport (= 6.1.7.8) 51 | globalid (>= 0.3.6) 52 | activemodel (6.1.7.8) 53 | activesupport (= 6.1.7.8) 54 | activerecord (6.1.7.8) 55 | activemodel (= 6.1.7.8) 56 | activesupport (= 6.1.7.8) 57 | activestorage (6.1.7.8) 58 | actionpack (= 6.1.7.8) 59 | activejob (= 6.1.7.8) 60 | activerecord (= 6.1.7.8) 61 | activesupport (= 6.1.7.8) 62 | marcel (~> 1.0) 63 | mini_mime (>= 1.1.0) 64 | activesupport (6.1.7.8) 65 | concurrent-ruby (~> 1.0, >= 1.0.2) 66 | i18n (>= 1.6, < 2) 67 | minitest (>= 5.1) 68 | tzinfo (~> 2.0) 69 | zeitwerk (~> 2.3) 70 | appraisal (2.5.0) 71 | bundler 72 | rake 73 | thor (>= 0.14.0) 74 | bigdecimal (3.1.8) 75 | builder (3.3.0) 76 | concurrent-ruby (1.3.3) 77 | crass (1.0.6) 78 | date (3.3.4) 79 | debug (1.9.2) 80 | irb (~> 1.10) 81 | reline (>= 0.3.8) 82 | erubi (1.13.0) 83 | globalid (1.2.1) 84 | activesupport (>= 6.1) 85 | google-protobuf (4.27.2) 86 | bigdecimal 87 | rake (>= 13) 88 | i18n (1.14.5) 89 | concurrent-ruby (~> 1.0) 90 | io-console (0.7.2) 91 | irb (1.14.0) 92 | rdoc (>= 4.0.0) 93 | reline (>= 0.4.2) 94 | loofah (2.22.0) 95 | crass (~> 1.0.2) 96 | nokogiri (>= 1.12.0) 97 | mail (2.8.1) 98 | mini_mime (>= 0.1.1) 99 | net-imap 100 | net-pop 101 | net-smtp 102 | marcel (1.0.4) 103 | method_source (1.1.0) 104 | mini_mime (1.1.5) 105 | mini_portile2 (2.8.7) 106 | minitest (5.24.1) 107 | net-imap (0.4.14) 108 | date 109 | net-protocol 110 | net-pop (0.1.2) 111 | net-protocol 112 | net-protocol (0.2.2) 113 | timeout 114 | net-smtp (0.5.0) 115 | net-protocol 116 | nio4r (2.7.3) 117 | nokogiri (1.16.7) 118 | mini_portile2 (~> 2.8.2) 119 | racc (~> 1.4) 120 | psych (5.1.2) 121 | stringio 122 | racc (1.8.1) 123 | rack (2.2.9) 124 | rack-test (2.1.0) 125 | rack (>= 1.3) 126 | rails (6.1.7.8) 127 | actioncable (= 6.1.7.8) 128 | actionmailbox (= 6.1.7.8) 129 | actionmailer (= 6.1.7.8) 130 | actionpack (= 6.1.7.8) 131 | actiontext (= 6.1.7.8) 132 | actionview (= 6.1.7.8) 133 | activejob (= 6.1.7.8) 134 | activemodel (= 6.1.7.8) 135 | activerecord (= 6.1.7.8) 136 | activestorage (= 6.1.7.8) 137 | activesupport (= 6.1.7.8) 138 | bundler (>= 1.15.0) 139 | railties (= 6.1.7.8) 140 | sprockets-rails (>= 2.0.0) 141 | rails-dom-testing (2.2.0) 142 | activesupport (>= 5.0.0) 143 | minitest 144 | nokogiri (>= 1.6) 145 | rails-html-sanitizer (1.6.0) 146 | loofah (~> 2.21) 147 | nokogiri (~> 1.14) 148 | railties (6.1.7.8) 149 | actionpack (= 6.1.7.8) 150 | activesupport (= 6.1.7.8) 151 | method_source 152 | rake (>= 12.2) 153 | thor (~> 1.0) 154 | rake (13.2.1) 155 | rdoc (6.7.0) 156 | psych (>= 4.0.0) 157 | reline (0.5.9) 158 | io-console (~> 0.5) 159 | sass-embedded (1.77.8) 160 | google-protobuf (~> 4.26) 161 | rake (>= 13) 162 | sprockets (4.2.1) 163 | concurrent-ruby (~> 1.0) 164 | rack (>= 2.2.4, < 4) 165 | sprockets-rails (3.5.1) 166 | actionpack (>= 6.1) 167 | activesupport (>= 6.1) 168 | sprockets (>= 3.0.0) 169 | sqlite3 (2.0.3) 170 | mini_portile2 (~> 2.8.0) 171 | stringio (3.1.1) 172 | thor (1.3.1) 173 | timeout (0.4.1) 174 | tzinfo (2.0.6) 175 | concurrent-ruby (~> 1.0) 176 | websocket-driver (0.7.6) 177 | websocket-extensions (>= 0.1.0) 178 | websocket-extensions (0.1.5) 179 | zeitwerk (2.6.17) 180 | 181 | PLATFORMS 182 | ruby 183 | 184 | DEPENDENCIES 185 | appraisal 186 | dartsass-rails! 187 | debug (>= 1.0.0) 188 | rails (~> 6.1.0) 189 | sqlite3 190 | 191 | BUNDLED WITH 192 | 2.2.32 193 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 David Heinemeier Hansson 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dart Sass for Rails 2 | 3 | [Sass](https://sass-lang.com) is a stylesheet language that’s compiled to CSS. It allows you to use variables, nested rules, mixins, functions, and more, all with a fully CSS-compatible syntax. 4 | 5 | This gem wraps [the standalone executable version](https://github.com/sass/dart-sass/releases) of the Dart version of Sass. The platform specific Dart Sass executables are distributed by [sass-embedded](https://rubygems.org/gems/sass-embedded) gem. 6 | 7 | The installer will create your default Sass input file in `app/assets/stylesheets/application.scss`. This is where you should import all the style files to be compiled [using the @use rule](https://sass-lang.com/documentation/at-rules/use). When you run `rails dartsass:build`, this input file will be used to generate the output in `app/assets/builds/application.css`. That's the output CSS that you'll include in your app. The load path for Sass is automatically configured to be `app/assets/stylesheets`. 8 | 9 | If you need to configure the build process – beyond configuring the build files – you can run `bundle exec dartsass` to access the platform-specific executable, and give it your own build options. 10 | 11 | When you're developing your application, you want to run Dart Sass in watch mode, so changes are automatically reflected in the generated CSS output. You can do this either by running `rails dartsass:watch` as a separate process, or by running `./bin/dev` which uses [foreman](https://github.com/ddollar/foreman) to start both the Dart Sass watch process and the rails server in development mode. 12 | 13 | 14 | ## Installation 15 | 16 | 1. Run `./bin/bundle add dartsass-rails` 17 | 2. Run `./bin/rails dartsass:install` 18 | 19 | ## Building in production 20 | 21 | The `dartsass:build` is automatically attached to `assets:precompile`, so before the asset pipeline digests the files, the Dart Sass output will be generated. 22 | 23 | ## Configuring builds 24 | 25 | By default, only `app/assets/stylesheets/application.scss` will be built. If you'd like to change the path of this stylesheet, add additional entry points, or customize the name of the built file, use the `Rails.application.config.dartsass.builds` configuration hash. 26 | 27 | 28 | ```ruby 29 | # config/initializers/dartsass.rb 30 | Rails.application.config.dartsass.builds = { 31 | "app/index.sass" => "app.css", 32 | "site.scss" => "site.css" 33 | } 34 | ``` 35 | 36 | The hash key is the relative path to a Sass file in `app/assets/stylesheets/` and the hash value will be the name of the file output to `app/assets/builds/`. 37 | 38 | If both the hash key and the hash value are directories instead of files, it configures a directory to directory compliation, which compiles all public Sass files whose filenames do not start with underscore (`_`). 39 | 40 | ```ruby 41 | # config/initializers/dartsass.rb 42 | Rails.application.config.dartsass.builds = { 43 | "." => "." 44 | } 45 | ``` 46 | 47 | ## Configuring build options 48 | 49 | By default, sass is invoked with `["--style=compressed", "--no-source-map"]`. You can adjust these options by overwriting `Rails.application.config.dartsass.build_options`. 50 | 51 | ```ruby 52 | # config/initializers/dartsass.rb 53 | Rails.application.config.dartsass.build_options << "--no-charset" << "--quiet-deps" 54 | ``` 55 | 56 | ## Importing assets from gems 57 | `dartsass:build` includes application [assets paths](https://guides.rubyonrails.org/asset_pipeline.html#search-paths) as Sass [load paths](https://sass-lang.com/documentation/at-rules/use#load-paths). Assuming the gem has made assets visible to the Rails application, no additional configuration is required to use them. 58 | 59 | ## Migrating from sass-rails 60 | 61 | If you're migrating from [sass-rails](https://github.com/rails/sass-rails) 62 | (applies to [sassc-rails](https://github.com/sass/sassc-rails) as well) 63 | and want to switch to dartsass-rails, follow these instructions below: 64 | 65 | 1. Remove the sass-rails gem from the Gemfile by running 66 | 67 | ``` 68 | ./bin/bundle remove sass-rails 69 | ``` 70 | 71 | 1. Install dartsass-rails by following the 72 | [Installation](#installation) instructions above 73 | 74 | 1. Remove any references to Sass files from the Sprockets manifest file: 75 | `app/assets/config/manifest.js` 76 | 77 | 1. In your continuous integration pipeline, before running any tests that 78 | interact with the browser, make sure to build the Sass files by running: 79 | 80 | ``` 81 | bundle exec rails dartsass:build 82 | ``` 83 | 84 | ## Troubleshooting 85 | 86 | Some common problems experienced by users: 87 | 88 | ### LoadError: cannot load such file -- sassc 89 | 90 | The reason for the above error is that Sprockets is trying to build Sass files 91 | but the sass-rails or sassc-rails gems are not installed. This is expected, 92 | since Dart Sass is used instead to build Sass files, and the solution is 93 | to make sure that Sprockets is not building any Sass files. 94 | 95 | There are three reasons why this error can occur: 96 | 97 | #### Sass files are referenced in the Sprockets manifest file 98 | 99 | If any Sass files are referenced in the Sprockets manifest file 100 | (`app/assets/config/manifest.js`) Sprockets will try to build the Sass files and 101 | fail. 102 | 103 | ##### Solution 104 | 105 | Remove any references to Sass files from the Sprockets manifest file. These are 106 | now handled by Dart Sass. If you have more Sass files than `application.scss`, 107 | make sure these are compiled by Dart Sass 108 | (see [Configuring builds](#configuring-builds) above). 109 | 110 | #### Running locally 111 | 112 | If you receive this error when running the Rails server locally and have 113 | already removed any references to Sass files from the Sprockets manifest file, 114 | the Dart Sass process is most likely not running. 115 | 116 | ##### Solution 117 | 118 | Make sure the Dart Sass process is running by starting the Rails server by 119 | running: `./bin/dev`. 120 | 121 | #### Running continuous integration pipelines 122 | 123 | If you receive this error when running tests that interact with the browser in 124 | a continuous integration pipeline and have removed any references to Sass files 125 | from the Sprockets manifest file, the Sass files have most likely not been 126 | built. 127 | 128 | ##### Solution 129 | 130 | Add a step to the continuous integration pipeline to build the Sass files with 131 | the following command: `bundle exec rails dartsass:build`. 132 | 133 | ## Version 134 | 135 | ``` sh 136 | bundle exec dartsass --version 137 | ``` 138 | 139 | ## License 140 | 141 | Dart Sass for Rails is released under the [MIT License](https://opensource.org/licenses/MIT). 142 | Sass is released under the [MIT License](https://opensource.org/licenses/MIT). 143 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/setup" 2 | 3 | require "bundler/gem_tasks" 4 | 5 | require "rake/testtask" 6 | 7 | Rake::TestTask.new(:test) do |t| 8 | t.libs << 'test' 9 | t.pattern = 'test/**/*_test.rb' 10 | t.verbose = false 11 | t.warning = true 12 | end 13 | 14 | task default: :test 15 | -------------------------------------------------------------------------------- /bin/release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | VERSION=$1 4 | 5 | if [ -z "$VERSION" ]; then 6 | echo "Usage: $0 " 7 | exit 1 8 | fi 9 | 10 | printf "module Dartsass\n VERSION = \"$VERSION\"\nend\n" > ./lib/dartsass/version.rb 11 | bundle 12 | git add Gemfile.lock lib/dartsass/version.rb 13 | git commit -m "Bump version for $VERSION" 14 | git push 15 | git tag v$VERSION 16 | git push --tags 17 | gem build dartsass-rails.gemspec 18 | gem push "dartsass-rails-$VERSION.gem" --host https://rubygems.org 19 | -------------------------------------------------------------------------------- /bin/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $: << File.expand_path("../test", __dir__) 3 | 4 | require "bundler/setup" 5 | require "rails/plugin/test" 6 | -------------------------------------------------------------------------------- /dartsass-rails.gemspec: -------------------------------------------------------------------------------- 1 | require_relative "lib/dartsass/version" 2 | 3 | Gem::Specification.new do |spec| 4 | spec.name = "dartsass-rails" 5 | spec.version = Dartsass::VERSION 6 | spec.authors = [ "David Heinemeier Hansson" ] 7 | spec.email = "david@hey.com" 8 | spec.homepage = "https://github.com/rails/dartsass-rails" 9 | spec.summary = "Integrate Dart Sass with the asset pipeline in Rails." 10 | spec.license = "MIT" 11 | 12 | spec.metadata = { 13 | "homepage_uri" => spec.homepage, 14 | "rubygems_mfa_required" => "true" 15 | } 16 | 17 | spec.files = Dir["{lib,exe}/**/*", "MIT-LICENSE", "Rakefile", "README.md"] 18 | spec.bindir = "exe" 19 | spec.executables << "dartsass" 20 | 21 | spec.add_dependency "railties", ">= 6.0.0" 22 | spec.add_dependency "sass-embedded", "~> 1.63" 23 | end 24 | -------------------------------------------------------------------------------- /exe/dartsass: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | load Gem.bin_path("sass-embedded", "sass") 5 | -------------------------------------------------------------------------------- /gemfiles/rails_7_0_propshaft.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal" 6 | gem "rails", "~> 7.0.0" 7 | gem "sqlite3" 8 | gem "debug", ">= 1.0.0" 9 | gem "propshaft" 10 | 11 | gemspec path: "../" 12 | -------------------------------------------------------------------------------- /gemfiles/rails_7_0_propshaft.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | dartsass-rails (0.5.0) 5 | railties (>= 6.0.0) 6 | sass-embedded (~> 1.63) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | actioncable (7.0.8) 12 | actionpack (= 7.0.8) 13 | activesupport (= 7.0.8) 14 | nio4r (~> 2.0) 15 | websocket-driver (>= 0.6.1) 16 | actionmailbox (7.0.8) 17 | actionpack (= 7.0.8) 18 | activejob (= 7.0.8) 19 | activerecord (= 7.0.8) 20 | activestorage (= 7.0.8) 21 | activesupport (= 7.0.8) 22 | mail (>= 2.7.1) 23 | net-imap 24 | net-pop 25 | net-smtp 26 | actionmailer (7.0.8) 27 | actionpack (= 7.0.8) 28 | actionview (= 7.0.8) 29 | activejob (= 7.0.8) 30 | activesupport (= 7.0.8) 31 | mail (~> 2.5, >= 2.5.4) 32 | net-imap 33 | net-pop 34 | net-smtp 35 | rails-dom-testing (~> 2.0) 36 | actionpack (7.0.8) 37 | actionview (= 7.0.8) 38 | activesupport (= 7.0.8) 39 | rack (~> 2.0, >= 2.2.4) 40 | rack-test (>= 0.6.3) 41 | rails-dom-testing (~> 2.0) 42 | rails-html-sanitizer (~> 1.0, >= 1.2.0) 43 | actiontext (7.0.8) 44 | actionpack (= 7.0.8) 45 | activerecord (= 7.0.8) 46 | activestorage (= 7.0.8) 47 | activesupport (= 7.0.8) 48 | globalid (>= 0.6.0) 49 | nokogiri (>= 1.8.5) 50 | actionview (7.0.8) 51 | activesupport (= 7.0.8) 52 | builder (~> 3.1) 53 | erubi (~> 1.4) 54 | rails-dom-testing (~> 2.0) 55 | rails-html-sanitizer (~> 1.1, >= 1.2.0) 56 | activejob (7.0.8) 57 | activesupport (= 7.0.8) 58 | globalid (>= 0.3.6) 59 | activemodel (7.0.8) 60 | activesupport (= 7.0.8) 61 | activerecord (7.0.8) 62 | activemodel (= 7.0.8) 63 | activesupport (= 7.0.8) 64 | activestorage (7.0.8) 65 | actionpack (= 7.0.8) 66 | activejob (= 7.0.8) 67 | activerecord (= 7.0.8) 68 | activesupport (= 7.0.8) 69 | marcel (~> 1.0) 70 | mini_mime (>= 1.1.0) 71 | activesupport (7.0.8) 72 | concurrent-ruby (~> 1.0, >= 1.0.2) 73 | i18n (>= 1.6, < 2) 74 | minitest (>= 5.1) 75 | tzinfo (~> 2.0) 76 | appraisal (2.5.0) 77 | bundler 78 | rake 79 | thor (>= 0.14.0) 80 | builder (3.2.4) 81 | concurrent-ruby (1.2.2) 82 | crass (1.0.6) 83 | date (3.3.4) 84 | debug (1.8.0) 85 | irb (>= 1.5.0) 86 | reline (>= 0.3.1) 87 | erubi (1.12.0) 88 | globalid (1.2.1) 89 | activesupport (>= 6.1) 90 | google-protobuf (3.25.1-x86_64-linux) 91 | i18n (1.14.1) 92 | concurrent-ruby (~> 1.0) 93 | io-console (0.6.0) 94 | irb (1.9.1) 95 | rdoc 96 | reline (>= 0.3.8) 97 | loofah (2.22.0) 98 | crass (~> 1.0.2) 99 | nokogiri (>= 1.12.0) 100 | mail (2.8.1) 101 | mini_mime (>= 0.1.1) 102 | net-imap 103 | net-pop 104 | net-smtp 105 | marcel (1.0.2) 106 | method_source (1.0.0) 107 | mini_mime (1.1.5) 108 | minitest (5.20.0) 109 | net-imap (0.4.7) 110 | date 111 | net-protocol 112 | net-pop (0.1.2) 113 | net-protocol 114 | net-protocol (0.2.2) 115 | timeout 116 | net-smtp (0.4.0) 117 | net-protocol 118 | nio4r (2.7.0) 119 | nokogiri (1.15.5-x86_64-linux) 120 | racc (~> 1.4) 121 | propshaft (0.8.0) 122 | actionpack (>= 7.0.0) 123 | activesupport (>= 7.0.0) 124 | rack 125 | railties (>= 7.0.0) 126 | psych (5.1.1.1) 127 | stringio 128 | racc (1.7.3) 129 | rack (2.2.8) 130 | rack-test (2.1.0) 131 | rack (>= 1.3) 132 | rails (7.0.8) 133 | actioncable (= 7.0.8) 134 | actionmailbox (= 7.0.8) 135 | actionmailer (= 7.0.8) 136 | actionpack (= 7.0.8) 137 | actiontext (= 7.0.8) 138 | actionview (= 7.0.8) 139 | activejob (= 7.0.8) 140 | activemodel (= 7.0.8) 141 | activerecord (= 7.0.8) 142 | activestorage (= 7.0.8) 143 | activesupport (= 7.0.8) 144 | bundler (>= 1.15.0) 145 | railties (= 7.0.8) 146 | rails-dom-testing (2.2.0) 147 | activesupport (>= 5.0.0) 148 | minitest 149 | nokogiri (>= 1.6) 150 | rails-html-sanitizer (1.6.0) 151 | loofah (~> 2.21) 152 | nokogiri (~> 1.14) 153 | railties (7.0.8) 154 | actionpack (= 7.0.8) 155 | activesupport (= 7.0.8) 156 | method_source 157 | rake (>= 12.2) 158 | thor (~> 1.0) 159 | zeitwerk (~> 2.5) 160 | rake (13.1.0) 161 | rdoc (6.6.0) 162 | psych (>= 4.0.0) 163 | reline (0.4.1) 164 | io-console (~> 0.5) 165 | sass-embedded (1.69.5-x86_64-linux-gnu) 166 | google-protobuf (~> 3.23) 167 | sqlite3 (1.6.9-x86_64-linux) 168 | stringio (3.1.0) 169 | thor (1.3.0) 170 | timeout (0.4.1) 171 | tzinfo (2.0.6) 172 | concurrent-ruby (~> 1.0) 173 | websocket-driver (0.7.6) 174 | websocket-extensions (>= 0.1.0) 175 | websocket-extensions (0.1.5) 176 | zeitwerk (2.6.12) 177 | 178 | PLATFORMS 179 | x86_64-linux 180 | 181 | DEPENDENCIES 182 | appraisal 183 | dartsass-rails! 184 | debug (>= 1.0.0) 185 | propshaft 186 | rails (~> 7.0.0) 187 | sqlite3 188 | 189 | BUNDLED WITH 190 | 2.2.32 191 | -------------------------------------------------------------------------------- /gemfiles/rails_7_0_sprockets.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal" 6 | gem "rails", "~> 7.0.0" 7 | gem "sqlite3" 8 | gem "debug", ">= 1.0.0" 9 | gem "sprockets-rails" 10 | 11 | gemspec path: "../" 12 | -------------------------------------------------------------------------------- /gemfiles/rails_7_0_sprockets.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | dartsass-rails (0.5.0) 5 | railties (>= 6.0.0) 6 | sass-embedded (~> 1.63) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | actioncable (7.0.8) 12 | actionpack (= 7.0.8) 13 | activesupport (= 7.0.8) 14 | nio4r (~> 2.0) 15 | websocket-driver (>= 0.6.1) 16 | actionmailbox (7.0.8) 17 | actionpack (= 7.0.8) 18 | activejob (= 7.0.8) 19 | activerecord (= 7.0.8) 20 | activestorage (= 7.0.8) 21 | activesupport (= 7.0.8) 22 | mail (>= 2.7.1) 23 | net-imap 24 | net-pop 25 | net-smtp 26 | actionmailer (7.0.8) 27 | actionpack (= 7.0.8) 28 | actionview (= 7.0.8) 29 | activejob (= 7.0.8) 30 | activesupport (= 7.0.8) 31 | mail (~> 2.5, >= 2.5.4) 32 | net-imap 33 | net-pop 34 | net-smtp 35 | rails-dom-testing (~> 2.0) 36 | actionpack (7.0.8) 37 | actionview (= 7.0.8) 38 | activesupport (= 7.0.8) 39 | rack (~> 2.0, >= 2.2.4) 40 | rack-test (>= 0.6.3) 41 | rails-dom-testing (~> 2.0) 42 | rails-html-sanitizer (~> 1.0, >= 1.2.0) 43 | actiontext (7.0.8) 44 | actionpack (= 7.0.8) 45 | activerecord (= 7.0.8) 46 | activestorage (= 7.0.8) 47 | activesupport (= 7.0.8) 48 | globalid (>= 0.6.0) 49 | nokogiri (>= 1.8.5) 50 | actionview (7.0.8) 51 | activesupport (= 7.0.8) 52 | builder (~> 3.1) 53 | erubi (~> 1.4) 54 | rails-dom-testing (~> 2.0) 55 | rails-html-sanitizer (~> 1.1, >= 1.2.0) 56 | activejob (7.0.8) 57 | activesupport (= 7.0.8) 58 | globalid (>= 0.3.6) 59 | activemodel (7.0.8) 60 | activesupport (= 7.0.8) 61 | activerecord (7.0.8) 62 | activemodel (= 7.0.8) 63 | activesupport (= 7.0.8) 64 | activestorage (7.0.8) 65 | actionpack (= 7.0.8) 66 | activejob (= 7.0.8) 67 | activerecord (= 7.0.8) 68 | activesupport (= 7.0.8) 69 | marcel (~> 1.0) 70 | mini_mime (>= 1.1.0) 71 | activesupport (7.0.8) 72 | concurrent-ruby (~> 1.0, >= 1.0.2) 73 | i18n (>= 1.6, < 2) 74 | minitest (>= 5.1) 75 | tzinfo (~> 2.0) 76 | appraisal (2.5.0) 77 | bundler 78 | rake 79 | thor (>= 0.14.0) 80 | builder (3.2.4) 81 | concurrent-ruby (1.2.2) 82 | crass (1.0.6) 83 | date (3.3.4) 84 | debug (1.8.0) 85 | irb (>= 1.5.0) 86 | reline (>= 0.3.1) 87 | erubi (1.12.0) 88 | globalid (1.2.1) 89 | activesupport (>= 6.1) 90 | google-protobuf (3.25.1-x86_64-linux) 91 | i18n (1.14.1) 92 | concurrent-ruby (~> 1.0) 93 | io-console (0.6.0) 94 | irb (1.9.1) 95 | rdoc 96 | reline (>= 0.3.8) 97 | loofah (2.22.0) 98 | crass (~> 1.0.2) 99 | nokogiri (>= 1.12.0) 100 | mail (2.8.1) 101 | mini_mime (>= 0.1.1) 102 | net-imap 103 | net-pop 104 | net-smtp 105 | marcel (1.0.2) 106 | method_source (1.0.0) 107 | mini_mime (1.1.5) 108 | minitest (5.20.0) 109 | net-imap (0.4.7) 110 | date 111 | net-protocol 112 | net-pop (0.1.2) 113 | net-protocol 114 | net-protocol (0.2.2) 115 | timeout 116 | net-smtp (0.4.0) 117 | net-protocol 118 | nio4r (2.7.0) 119 | nokogiri (1.15.5-x86_64-linux) 120 | racc (~> 1.4) 121 | psych (5.1.1.1) 122 | stringio 123 | racc (1.7.3) 124 | rack (2.2.8) 125 | rack-test (2.1.0) 126 | rack (>= 1.3) 127 | rails (7.0.8) 128 | actioncable (= 7.0.8) 129 | actionmailbox (= 7.0.8) 130 | actionmailer (= 7.0.8) 131 | actionpack (= 7.0.8) 132 | actiontext (= 7.0.8) 133 | actionview (= 7.0.8) 134 | activejob (= 7.0.8) 135 | activemodel (= 7.0.8) 136 | activerecord (= 7.0.8) 137 | activestorage (= 7.0.8) 138 | activesupport (= 7.0.8) 139 | bundler (>= 1.15.0) 140 | railties (= 7.0.8) 141 | rails-dom-testing (2.2.0) 142 | activesupport (>= 5.0.0) 143 | minitest 144 | nokogiri (>= 1.6) 145 | rails-html-sanitizer (1.6.0) 146 | loofah (~> 2.21) 147 | nokogiri (~> 1.14) 148 | railties (7.0.8) 149 | actionpack (= 7.0.8) 150 | activesupport (= 7.0.8) 151 | method_source 152 | rake (>= 12.2) 153 | thor (~> 1.0) 154 | zeitwerk (~> 2.5) 155 | rake (13.1.0) 156 | rdoc (6.6.0) 157 | psych (>= 4.0.0) 158 | reline (0.4.1) 159 | io-console (~> 0.5) 160 | sass-embedded (1.69.5-x86_64-linux-gnu) 161 | google-protobuf (~> 3.23) 162 | sprockets (4.2.1) 163 | concurrent-ruby (~> 1.0) 164 | rack (>= 2.2.4, < 4) 165 | sprockets-rails (3.4.2) 166 | actionpack (>= 5.2) 167 | activesupport (>= 5.2) 168 | sprockets (>= 3.0.0) 169 | sqlite3 (1.6.9-x86_64-linux) 170 | stringio (3.1.0) 171 | thor (1.3.0) 172 | timeout (0.4.1) 173 | tzinfo (2.0.6) 174 | concurrent-ruby (~> 1.0) 175 | websocket-driver (0.7.6) 176 | websocket-extensions (>= 0.1.0) 177 | websocket-extensions (0.1.5) 178 | zeitwerk (2.6.12) 179 | 180 | PLATFORMS 181 | x86_64-linux 182 | 183 | DEPENDENCIES 184 | appraisal 185 | dartsass-rails! 186 | debug (>= 1.0.0) 187 | rails (~> 7.0.0) 188 | sprockets-rails 189 | sqlite3 190 | 191 | BUNDLED WITH 192 | 2.2.32 193 | -------------------------------------------------------------------------------- /gemfiles/rails_7_1_propshaft.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal" 6 | gem "rails", "~> 7.1.0" 7 | gem "sqlite3" 8 | gem "debug", ">= 1.0.0" 9 | gem "propshaft" 10 | 11 | gemspec path: "../" 12 | -------------------------------------------------------------------------------- /gemfiles/rails_7_1_propshaft.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | dartsass-rails (0.5.0) 5 | railties (>= 6.0.0) 6 | sass-embedded (~> 1.63) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | actioncable (7.1.2) 12 | actionpack (= 7.1.2) 13 | activesupport (= 7.1.2) 14 | nio4r (~> 2.0) 15 | websocket-driver (>= 0.6.1) 16 | zeitwerk (~> 2.6) 17 | actionmailbox (7.1.2) 18 | actionpack (= 7.1.2) 19 | activejob (= 7.1.2) 20 | activerecord (= 7.1.2) 21 | activestorage (= 7.1.2) 22 | activesupport (= 7.1.2) 23 | mail (>= 2.7.1) 24 | net-imap 25 | net-pop 26 | net-smtp 27 | actionmailer (7.1.2) 28 | actionpack (= 7.1.2) 29 | actionview (= 7.1.2) 30 | activejob (= 7.1.2) 31 | activesupport (= 7.1.2) 32 | mail (~> 2.5, >= 2.5.4) 33 | net-imap 34 | net-pop 35 | net-smtp 36 | rails-dom-testing (~> 2.2) 37 | actionpack (7.1.2) 38 | actionview (= 7.1.2) 39 | activesupport (= 7.1.2) 40 | nokogiri (>= 1.8.5) 41 | racc 42 | rack (>= 2.2.4) 43 | rack-session (>= 1.0.1) 44 | rack-test (>= 0.6.3) 45 | rails-dom-testing (~> 2.2) 46 | rails-html-sanitizer (~> 1.6) 47 | actiontext (7.1.2) 48 | actionpack (= 7.1.2) 49 | activerecord (= 7.1.2) 50 | activestorage (= 7.1.2) 51 | activesupport (= 7.1.2) 52 | globalid (>= 0.6.0) 53 | nokogiri (>= 1.8.5) 54 | actionview (7.1.2) 55 | activesupport (= 7.1.2) 56 | builder (~> 3.1) 57 | erubi (~> 1.11) 58 | rails-dom-testing (~> 2.2) 59 | rails-html-sanitizer (~> 1.6) 60 | activejob (7.1.2) 61 | activesupport (= 7.1.2) 62 | globalid (>= 0.3.6) 63 | activemodel (7.1.2) 64 | activesupport (= 7.1.2) 65 | activerecord (7.1.2) 66 | activemodel (= 7.1.2) 67 | activesupport (= 7.1.2) 68 | timeout (>= 0.4.0) 69 | activestorage (7.1.2) 70 | actionpack (= 7.1.2) 71 | activejob (= 7.1.2) 72 | activerecord (= 7.1.2) 73 | activesupport (= 7.1.2) 74 | marcel (~> 1.0) 75 | activesupport (7.1.2) 76 | base64 77 | bigdecimal 78 | concurrent-ruby (~> 1.0, >= 1.0.2) 79 | connection_pool (>= 2.2.5) 80 | drb 81 | i18n (>= 1.6, < 2) 82 | minitest (>= 5.1) 83 | mutex_m 84 | tzinfo (~> 2.0) 85 | appraisal (2.5.0) 86 | bundler 87 | rake 88 | thor (>= 0.14.0) 89 | base64 (0.2.0) 90 | bigdecimal (3.1.4) 91 | builder (3.2.4) 92 | concurrent-ruby (1.2.2) 93 | connection_pool (2.4.1) 94 | crass (1.0.6) 95 | date (3.3.4) 96 | debug (1.8.0) 97 | irb (>= 1.5.0) 98 | reline (>= 0.3.1) 99 | drb (2.2.0) 100 | ruby2_keywords 101 | erubi (1.12.0) 102 | globalid (1.2.1) 103 | activesupport (>= 6.1) 104 | google-protobuf (3.25.1-x86_64-linux) 105 | i18n (1.14.1) 106 | concurrent-ruby (~> 1.0) 107 | io-console (0.6.0) 108 | irb (1.9.1) 109 | rdoc 110 | reline (>= 0.3.8) 111 | loofah (2.22.0) 112 | crass (~> 1.0.2) 113 | nokogiri (>= 1.12.0) 114 | mail (2.8.1) 115 | mini_mime (>= 0.1.1) 116 | net-imap 117 | net-pop 118 | net-smtp 119 | marcel (1.0.2) 120 | mini_mime (1.1.5) 121 | minitest (5.20.0) 122 | mutex_m (0.2.0) 123 | net-imap (0.4.7) 124 | date 125 | net-protocol 126 | net-pop (0.1.2) 127 | net-protocol 128 | net-protocol (0.2.2) 129 | timeout 130 | net-smtp (0.4.0) 131 | net-protocol 132 | nio4r (2.7.0) 133 | nokogiri (1.15.5-x86_64-linux) 134 | racc (~> 1.4) 135 | propshaft (0.8.0) 136 | actionpack (>= 7.0.0) 137 | activesupport (>= 7.0.0) 138 | rack 139 | railties (>= 7.0.0) 140 | psych (5.1.1.1) 141 | stringio 142 | racc (1.7.3) 143 | rack (3.0.8) 144 | rack-session (2.0.0) 145 | rack (>= 3.0.0) 146 | rack-test (2.1.0) 147 | rack (>= 1.3) 148 | rackup (2.1.0) 149 | rack (>= 3) 150 | webrick (~> 1.8) 151 | rails (7.1.2) 152 | actioncable (= 7.1.2) 153 | actionmailbox (= 7.1.2) 154 | actionmailer (= 7.1.2) 155 | actionpack (= 7.1.2) 156 | actiontext (= 7.1.2) 157 | actionview (= 7.1.2) 158 | activejob (= 7.1.2) 159 | activemodel (= 7.1.2) 160 | activerecord (= 7.1.2) 161 | activestorage (= 7.1.2) 162 | activesupport (= 7.1.2) 163 | bundler (>= 1.15.0) 164 | railties (= 7.1.2) 165 | rails-dom-testing (2.2.0) 166 | activesupport (>= 5.0.0) 167 | minitest 168 | nokogiri (>= 1.6) 169 | rails-html-sanitizer (1.6.0) 170 | loofah (~> 2.21) 171 | nokogiri (~> 1.14) 172 | railties (7.1.2) 173 | actionpack (= 7.1.2) 174 | activesupport (= 7.1.2) 175 | irb 176 | rackup (>= 1.0.0) 177 | rake (>= 12.2) 178 | thor (~> 1.0, >= 1.2.2) 179 | zeitwerk (~> 2.6) 180 | rake (13.1.0) 181 | rdoc (6.6.0) 182 | psych (>= 4.0.0) 183 | reline (0.4.1) 184 | io-console (~> 0.5) 185 | ruby2_keywords (0.0.5) 186 | sass-embedded (1.69.5-x86_64-linux-gnu) 187 | google-protobuf (~> 3.23) 188 | sqlite3 (1.6.9-x86_64-linux) 189 | stringio (3.1.0) 190 | thor (1.3.0) 191 | timeout (0.4.1) 192 | tzinfo (2.0.6) 193 | concurrent-ruby (~> 1.0) 194 | webrick (1.8.1) 195 | websocket-driver (0.7.6) 196 | websocket-extensions (>= 0.1.0) 197 | websocket-extensions (0.1.5) 198 | zeitwerk (2.6.12) 199 | 200 | PLATFORMS 201 | x86_64-linux 202 | 203 | DEPENDENCIES 204 | appraisal 205 | dartsass-rails! 206 | debug (>= 1.0.0) 207 | propshaft 208 | rails (~> 7.1.0) 209 | sqlite3 210 | 211 | BUNDLED WITH 212 | 2.2.32 213 | -------------------------------------------------------------------------------- /gemfiles/rails_7_1_sprockets.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal" 6 | gem "rails", "~> 7.1.0" 7 | gem "sqlite3" 8 | gem "debug", ">= 1.0.0" 9 | gem "sprockets-rails" 10 | 11 | gemspec path: "../" 12 | -------------------------------------------------------------------------------- /gemfiles/rails_7_1_sprockets.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: .. 3 | specs: 4 | dartsass-rails (0.5.0) 5 | railties (>= 6.0.0) 6 | sass-embedded (~> 1.63) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | actioncable (7.1.2) 12 | actionpack (= 7.1.2) 13 | activesupport (= 7.1.2) 14 | nio4r (~> 2.0) 15 | websocket-driver (>= 0.6.1) 16 | zeitwerk (~> 2.6) 17 | actionmailbox (7.1.2) 18 | actionpack (= 7.1.2) 19 | activejob (= 7.1.2) 20 | activerecord (= 7.1.2) 21 | activestorage (= 7.1.2) 22 | activesupport (= 7.1.2) 23 | mail (>= 2.7.1) 24 | net-imap 25 | net-pop 26 | net-smtp 27 | actionmailer (7.1.2) 28 | actionpack (= 7.1.2) 29 | actionview (= 7.1.2) 30 | activejob (= 7.1.2) 31 | activesupport (= 7.1.2) 32 | mail (~> 2.5, >= 2.5.4) 33 | net-imap 34 | net-pop 35 | net-smtp 36 | rails-dom-testing (~> 2.2) 37 | actionpack (7.1.2) 38 | actionview (= 7.1.2) 39 | activesupport (= 7.1.2) 40 | nokogiri (>= 1.8.5) 41 | racc 42 | rack (>= 2.2.4) 43 | rack-session (>= 1.0.1) 44 | rack-test (>= 0.6.3) 45 | rails-dom-testing (~> 2.2) 46 | rails-html-sanitizer (~> 1.6) 47 | actiontext (7.1.2) 48 | actionpack (= 7.1.2) 49 | activerecord (= 7.1.2) 50 | activestorage (= 7.1.2) 51 | activesupport (= 7.1.2) 52 | globalid (>= 0.6.0) 53 | nokogiri (>= 1.8.5) 54 | actionview (7.1.2) 55 | activesupport (= 7.1.2) 56 | builder (~> 3.1) 57 | erubi (~> 1.11) 58 | rails-dom-testing (~> 2.2) 59 | rails-html-sanitizer (~> 1.6) 60 | activejob (7.1.2) 61 | activesupport (= 7.1.2) 62 | globalid (>= 0.3.6) 63 | activemodel (7.1.2) 64 | activesupport (= 7.1.2) 65 | activerecord (7.1.2) 66 | activemodel (= 7.1.2) 67 | activesupport (= 7.1.2) 68 | timeout (>= 0.4.0) 69 | activestorage (7.1.2) 70 | actionpack (= 7.1.2) 71 | activejob (= 7.1.2) 72 | activerecord (= 7.1.2) 73 | activesupport (= 7.1.2) 74 | marcel (~> 1.0) 75 | activesupport (7.1.2) 76 | base64 77 | bigdecimal 78 | concurrent-ruby (~> 1.0, >= 1.0.2) 79 | connection_pool (>= 2.2.5) 80 | drb 81 | i18n (>= 1.6, < 2) 82 | minitest (>= 5.1) 83 | mutex_m 84 | tzinfo (~> 2.0) 85 | appraisal (2.5.0) 86 | bundler 87 | rake 88 | thor (>= 0.14.0) 89 | base64 (0.2.0) 90 | bigdecimal (3.1.4) 91 | builder (3.2.4) 92 | concurrent-ruby (1.2.2) 93 | connection_pool (2.4.1) 94 | crass (1.0.6) 95 | date (3.3.4) 96 | debug (1.8.0) 97 | irb (>= 1.5.0) 98 | reline (>= 0.3.1) 99 | drb (2.2.0) 100 | ruby2_keywords 101 | erubi (1.12.0) 102 | globalid (1.2.1) 103 | activesupport (>= 6.1) 104 | google-protobuf (3.25.1-x86_64-linux) 105 | i18n (1.14.1) 106 | concurrent-ruby (~> 1.0) 107 | io-console (0.6.0) 108 | irb (1.9.1) 109 | rdoc 110 | reline (>= 0.3.8) 111 | loofah (2.22.0) 112 | crass (~> 1.0.2) 113 | nokogiri (>= 1.12.0) 114 | mail (2.8.1) 115 | mini_mime (>= 0.1.1) 116 | net-imap 117 | net-pop 118 | net-smtp 119 | marcel (1.0.2) 120 | mini_mime (1.1.5) 121 | minitest (5.20.0) 122 | mutex_m (0.2.0) 123 | net-imap (0.4.7) 124 | date 125 | net-protocol 126 | net-pop (0.1.2) 127 | net-protocol 128 | net-protocol (0.2.2) 129 | timeout 130 | net-smtp (0.4.0) 131 | net-protocol 132 | nio4r (2.7.0) 133 | nokogiri (1.15.5-x86_64-linux) 134 | racc (~> 1.4) 135 | psych (5.1.1.1) 136 | stringio 137 | racc (1.7.3) 138 | rack (3.0.8) 139 | rack-session (2.0.0) 140 | rack (>= 3.0.0) 141 | rack-test (2.1.0) 142 | rack (>= 1.3) 143 | rackup (2.1.0) 144 | rack (>= 3) 145 | webrick (~> 1.8) 146 | rails (7.1.2) 147 | actioncable (= 7.1.2) 148 | actionmailbox (= 7.1.2) 149 | actionmailer (= 7.1.2) 150 | actionpack (= 7.1.2) 151 | actiontext (= 7.1.2) 152 | actionview (= 7.1.2) 153 | activejob (= 7.1.2) 154 | activemodel (= 7.1.2) 155 | activerecord (= 7.1.2) 156 | activestorage (= 7.1.2) 157 | activesupport (= 7.1.2) 158 | bundler (>= 1.15.0) 159 | railties (= 7.1.2) 160 | rails-dom-testing (2.2.0) 161 | activesupport (>= 5.0.0) 162 | minitest 163 | nokogiri (>= 1.6) 164 | rails-html-sanitizer (1.6.0) 165 | loofah (~> 2.21) 166 | nokogiri (~> 1.14) 167 | railties (7.1.2) 168 | actionpack (= 7.1.2) 169 | activesupport (= 7.1.2) 170 | irb 171 | rackup (>= 1.0.0) 172 | rake (>= 12.2) 173 | thor (~> 1.0, >= 1.2.2) 174 | zeitwerk (~> 2.6) 175 | rake (13.1.0) 176 | rdoc (6.6.0) 177 | psych (>= 4.0.0) 178 | reline (0.4.1) 179 | io-console (~> 0.5) 180 | ruby2_keywords (0.0.5) 181 | sass-embedded (1.69.5-x86_64-linux-gnu) 182 | google-protobuf (~> 3.23) 183 | sprockets (4.2.1) 184 | concurrent-ruby (~> 1.0) 185 | rack (>= 2.2.4, < 4) 186 | sprockets-rails (3.4.2) 187 | actionpack (>= 5.2) 188 | activesupport (>= 5.2) 189 | sprockets (>= 3.0.0) 190 | sqlite3 (1.6.9-x86_64-linux) 191 | stringio (3.1.0) 192 | thor (1.3.0) 193 | timeout (0.4.1) 194 | tzinfo (2.0.6) 195 | concurrent-ruby (~> 1.0) 196 | webrick (1.8.1) 197 | websocket-driver (0.7.6) 198 | websocket-extensions (>= 0.1.0) 199 | websocket-extensions (0.1.5) 200 | zeitwerk (2.6.12) 201 | 202 | PLATFORMS 203 | x86_64-linux 204 | 205 | DEPENDENCIES 206 | appraisal 207 | dartsass-rails! 208 | debug (>= 1.0.0) 209 | rails (~> 7.1.0) 210 | sprockets-rails 211 | sqlite3 212 | 213 | BUNDLED WITH 214 | 2.2.32 215 | -------------------------------------------------------------------------------- /gemfiles/rails_main_propshaft.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal" 6 | gem "rails", branch: "main", git: "https://github.com/rails/rails.git" 7 | gem "sqlite3" 8 | gem "debug", ">= 1.0.0" 9 | gem "propshaft" 10 | 11 | gemspec path: "../" 12 | -------------------------------------------------------------------------------- /gemfiles/rails_main_sprockets.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "appraisal" 6 | gem "rails", branch: "main", git: "https://github.com/rails/rails.git" 7 | gem "sqlite3" 8 | gem "debug", ">= 1.0.0" 9 | gem "sprockets-rails" 10 | 11 | gemspec path: "../" 12 | -------------------------------------------------------------------------------- /lib/dartsass-rails.rb: -------------------------------------------------------------------------------- 1 | module Dartsass 2 | end 3 | 4 | require "dartsass/version" 5 | require "dartsass/engine" 6 | -------------------------------------------------------------------------------- /lib/dartsass/engine.rb: -------------------------------------------------------------------------------- 1 | require "rails" 2 | 3 | module Dartsass 4 | class Engine < ::Rails::Engine 5 | config.dartsass = ActiveSupport::OrderedOptions.new 6 | config.dartsass.builds = { "application.scss" => "application.css" } 7 | config.dartsass.build_options = ["--style=compressed", "--no-source-map"] 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/dartsass/runner.rb: -------------------------------------------------------------------------------- 1 | module Dartsass 2 | module Runner 3 | EXEC_PATH = "#{Pathname.new(__dir__).to_s}/../../exe/dartsass" 4 | CSS_LOAD_PATH = Rails.root.join("app/assets/stylesheets") 5 | CSS_BUILD_PATH = Rails.root.join("app/assets/builds") 6 | 7 | module_function 8 | 9 | def dartsass_build_mapping 10 | Rails.application.config.dartsass.builds.map { |input, output| 11 | "#{CSS_LOAD_PATH.join(input)}:#{CSS_BUILD_PATH.join(output)}" 12 | } 13 | end 14 | 15 | def dartsass_build_options 16 | Rails.application.config.dartsass.build_options.flat_map(&:split) 17 | end 18 | 19 | def dartsass_load_paths 20 | [ CSS_LOAD_PATH ].concat(Rails.application.config.assets.paths).flat_map { |path| ["--load-path", path.to_s] } 21 | end 22 | 23 | def dartsass_compile_command 24 | [ RbConfig.ruby, EXEC_PATH ].concat(dartsass_build_options).concat(dartsass_load_paths).concat(dartsass_build_mapping) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/dartsass/version.rb: -------------------------------------------------------------------------------- 1 | module Dartsass 2 | VERSION = "0.5.1" 3 | end 4 | -------------------------------------------------------------------------------- /lib/install/Procfile.dev: -------------------------------------------------------------------------------- 1 | web: bin/rails server -p 3000 2 | css: bin/rails dartsass:watch 3 | -------------------------------------------------------------------------------- /lib/install/application.scss: -------------------------------------------------------------------------------- 1 | // Sassy 2 | -------------------------------------------------------------------------------- /lib/install/dartsass.rb: -------------------------------------------------------------------------------- 1 | APPLICATION_LAYOUT_PATH = Rails.root.join("app/views/layouts/application.html.erb") 2 | CENTERING_CONTAINER_INSERTION_POINT = /^\s*<%= yield %>/.freeze 3 | 4 | say "Build into app/assets/builds" 5 | empty_directory "app/assets/builds" 6 | keep_file "app/assets/builds" 7 | 8 | if (sprockets_manifest_path = Rails.root.join("app/assets/config/manifest.js")).exist? 9 | append_to_file sprockets_manifest_path, %(//= link_tree ../builds\n) 10 | 11 | say "Stop linking stylesheets automatically" 12 | gsub_file "app/assets/config/manifest.js", "//= link_directory ../stylesheets .css\n", "" 13 | end 14 | 15 | if Rails.root.join(".gitignore").exist? 16 | append_to_file(".gitignore", %(\n/app/assets/builds/*\n!/app/assets/builds/.keep\n)) 17 | end 18 | 19 | unless Rails.root.join("app/assets/stylesheets/application.scss").exist? 20 | say "Add default app/assets/stylesheets/application.scss" 21 | copy_file "#{__dir__}/application.scss", "app/assets/stylesheets/application.scss" 22 | end 23 | 24 | if Rails.root.join("Procfile.dev").exist? 25 | append_to_file "Procfile.dev", "css: bin/rails dartsass:watch\n" 26 | else 27 | say "Add default Procfile.dev" 28 | copy_file "#{__dir__}/Procfile.dev", "Procfile.dev" 29 | 30 | say "Ensure foreman is installed" 31 | run "gem install foreman" 32 | end 33 | 34 | say "Add bin/dev to start foreman" 35 | copy_file "#{__dir__}/dev", "bin/dev", force: true 36 | chmod "bin/dev", 0755, verbose: false 37 | 38 | say "Compile initial Dart Sass build" 39 | run "rails dartsass:build" 40 | -------------------------------------------------------------------------------- /lib/install/dev: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | if ! gem list foreman -i --silent; then 4 | echo "Installing foreman..." 5 | gem install foreman 6 | fi 7 | 8 | exec foreman start -f Procfile.dev "$@" 9 | -------------------------------------------------------------------------------- /lib/tasks/build.rake: -------------------------------------------------------------------------------- 1 | require "dartsass/runner" 2 | 3 | namespace :dartsass do 4 | desc "Build your Dart Sass CSS" 5 | task build: :environment do 6 | system(*Dartsass::Runner.dartsass_compile_command, exception: true) 7 | end 8 | 9 | desc "Watch and build your Dart Sass CSS on file changes" 10 | task watch: :environment do 11 | system(*Dartsass::Runner.dartsass_compile_command, "--watch", exception: true) 12 | end 13 | end 14 | 15 | unless ENV["SKIP_CSS_BUILD"] 16 | Rake::Task["assets:precompile"].enhance(["dartsass:build"]) 17 | 18 | if Rake::Task.task_defined?("test:prepare") 19 | Rake::Task["test:prepare"].enhance(["dartsass:build"]) 20 | elsif Rake::Task.task_defined?("db:test:prepare") 21 | Rake::Task["db:test:prepare"].enhance(["dartsass:build"]) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/tasks/clobber.rake: -------------------------------------------------------------------------------- 1 | namespace :dartsass do 2 | desc "Remove CSS builds" 3 | task :clobber do 4 | rm_rf Dir["app/assets/builds/**/[^.]*.css"], verbose: false 5 | rm_rf Dir["app/assets/builds/**/[^.]*.css\.map"], verbose: false 6 | end 7 | end 8 | 9 | Rake::Task["assets:clobber"].enhance(["dartsass:clobber"]) 10 | -------------------------------------------------------------------------------- /lib/tasks/install.rake: -------------------------------------------------------------------------------- 1 | namespace :dartsass do 2 | desc "Install Dart Sass into the app" 3 | task :install do 4 | system RbConfig.ruby, "./bin/rails", "app:template", "LOCATION=#{File.expand_path("../install/dartsass.rb", __dir__)}", exception: true 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/installer_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class InstallerTest < ActiveSupport::TestCase 4 | include RailsAppHelpers 5 | 6 | test "installer" do 7 | with_new_rails_app do 8 | out, _err = run_command("bin/rails", "dartsass:install") 9 | 10 | assert_match "", out 11 | assert_equal 0, File.size("app/assets/builds/.keep") 12 | assert_match "/app/assets/builds/*\n!/app/assets/builds/.keep", File.read(".gitignore") 13 | assert_equal File.read("#{__dir__}/../lib/install/application.scss"), File.read("app/assets/stylesheets/application.scss") 14 | assert_equal File.read("#{__dir__}/../lib/install/Procfile.dev"), File.read("Procfile.dev") 15 | assert_equal File.read("#{__dir__}/../lib/install/dev"), File.read("bin/dev") 16 | assert_equal 0700, File.stat("bin/dev").mode & 0700 17 | 18 | if sprockets? 19 | assert_match "//= link_tree ../builds", File.read("app/assets/config/manifest.js") 20 | assert_no_match "//= link_directory ../stylesheets .css", File.read("app/assets/config/manifest.js") 21 | end 22 | end 23 | end 24 | 25 | test "installer with missing .gitignore" do 26 | with_new_rails_app do 27 | FileUtils.rm(".gitignore") 28 | out, _err = run_command("bin/rails", "dartsass:install") 29 | 30 | assert_match "", out 31 | assert_not File.exist?(".gitignore") 32 | end 33 | end 34 | 35 | test "installer with pre-existing application.scss" do 36 | with_new_rails_app do 37 | File.write("app/assets/stylesheets/application.scss", "// pre-existing") 38 | out, _err = run_command("bin/rails", "dartsass:install") 39 | 40 | assert_match "", out 41 | assert_equal "// pre-existing", File.read("app/assets/stylesheets/application.scss") 42 | end 43 | end 44 | 45 | test "installer with pre-existing Procfile" do 46 | with_new_rails_app do 47 | File.write("Procfile.dev", "pre: existing\n") 48 | out, _err = run_command("bin/rails", "dartsass:install") 49 | 50 | assert_match "", out 51 | assert_equal "pre: existing\ncss: bin/rails dartsass:watch\n", File.read("Procfile.dev") 52 | end 53 | end 54 | 55 | private 56 | def with_new_rails_app(&block) 57 | super do 58 | # Override `dartsass:build` to check that installation completed and to reduce test run time. 59 | File.write("Rakefile", <<~RAKEFILE, mode: "a+") 60 | Rake::Task["dartsass:build"].clear 61 | namespace :dartsass do 62 | task build: :environment do 63 | puts "" 64 | end 65 | end 66 | RAKEFILE 67 | 68 | block.call 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # Configure Rails Environment 2 | ENV["RAILS_ENV"] = "test" 3 | 4 | require "fileutils" 5 | require "rails" 6 | require "rails/test_help" 7 | 8 | module RailsAppHelpers 9 | def self.included(base) 10 | base.include ActiveSupport::Testing::Isolation 11 | end 12 | 13 | private 14 | def sprockets? 15 | Gem.loaded_specs.key?("sprockets-rails") 16 | end 17 | 18 | def propshaft? 19 | Gem.loaded_specs.key?("propshaft") 20 | end 21 | 22 | def asset_pipeline_option 23 | if Rails::VERSION::MAJOR >= 7 24 | if propshaft? 25 | "--asset-pipeline=propshaft" 26 | elsif sprockets? 27 | "--asset-pipeline=sprockets" 28 | end 29 | end 30 | end 31 | 32 | def create_new_rails_app(app_dir) 33 | require "rails/generators/rails/app/app_generator" 34 | Rails::Generators::AppGenerator.start([app_dir, *asset_pipeline_option, "--skip-bundle", "--skip-bootsnap", "--quiet"]) 35 | 36 | Dir.chdir(app_dir) do 37 | gemfile = File.read("Gemfile") 38 | 39 | gemfile.gsub!(/^gem ["']sassc?-rails["'].*/, "") # for Rails 6.1 and 7.0 40 | gemfile.gsub!(/^gem ["']dartsass-rails["'].*/, "") 41 | gemfile << %(gem "dartsass-rails", path: #{File.expand_path("..", __dir__).inspect}\n) 42 | 43 | if Rails::VERSION::PRE == "alpha" 44 | gemfile.gsub!(/^gem ["']rails["'].*/, "") 45 | gemfile << %(gem "rails", path: #{Gem.loaded_specs["rails"].full_gem_path.inspect}\n) 46 | end 47 | 48 | File.write("Gemfile", gemfile) 49 | 50 | run_command("bundle", "install") 51 | end 52 | end 53 | 54 | def with_new_rails_app(&block) 55 | require "digest/sha1" 56 | variant = [RUBY_VERSION, Gem.loaded_specs["rails"].full_gem_path, asset_pipeline_option] 57 | app_name = "app_#{Digest::SHA1.hexdigest(variant.to_s)}" 58 | cache_dir = "#{__dir__}/../tmp" 59 | FileUtils.mkdir_p(cache_dir) 60 | 61 | Dir.mktmpdir do |tmpdir| 62 | if Dir.exist?("#{cache_dir}/#{app_name}") 63 | FileUtils.cp_r("#{cache_dir}/#{app_name}", tmpdir) 64 | else 65 | create_new_rails_app("#{tmpdir}/#{app_name}") 66 | FileUtils.cp_r("#{tmpdir}/#{app_name}", cache_dir) # Cache app for future runs. 67 | end 68 | 69 | Dir.chdir("#{tmpdir}/#{app_name}", &block) 70 | end 71 | end 72 | 73 | def run_command(*command) 74 | Bundler.with_unbundled_env do 75 | capture_subprocess_io { system(*command, exception: true) } 76 | end 77 | end 78 | end 79 | --------------------------------------------------------------------------------