GithubHelp home page GithubHelp logo

rails / propshaft Goto Github PK

View Code? Open in Web Editor NEW
821.0 33.0 90.0 483 KB

Deliver assets for Rails

License: MIT License

Ruby 93.74% Shell 0.62% Dockerfile 4.43% JavaScript 0.04% CSS 0.22% HTML 0.95%

propshaft's Introduction

Propshaft

Propshaft is an asset pipeline library for Rails. It's built for an era where bundling assets to save on HTTP connections is no longer urgent, where JavaScript and CSS are either compiled by dedicated Node.js bundlers or served directly to the browsers, and where increases in bandwidth have made the need for minification less pressing. These factors allow for a dramatically simpler and faster asset pipeline compared to previous options, like Sprockets.

So that's what Propshaft doesn't do. Here's what it does provide:

  1. Configurable load path: You can register directories from multiple places in your app and gems, and reference assets from all of these paths as though they were one.
  2. Digest stamping: All assets in the load path will be copied (or compiled) in a precompilation step for production that also stamps all of them with a digest hash, so you can use long-expiry cache headers for better performance. The digested assets can be referred to through their logical path because the processing leaves a manifest file that provides a way to translate.
  3. Development server: There's no need to precompile the assets in development. You can refer to them via the same asset_path helpers and they'll be served by a development server.
  4. Basic compilers: Propshaft was explicitly not designed to provide full transpiler capabilities. You can get that better elsewhere. But it does offer a simple input->output compiler setup that by default is used to translate url(asset) function calls in CSS to url(digested-asset) instead and source mapping comments likewise.

Installation

With Rails 7+, you can start a new application with propshaft using rails new myapp -a propshaft. For existing applications, check the upgrade guide which contains step-by-step instructions.

Usage

Propshaft makes all the assets from all the paths it's been configured with through config.assets.paths available for serving and will copy all of them into public/assets when precompiling. This is unlike Sprockets, which did not copy over assets that hadn't been explicitly included in one of the bundled assets.

You can however exempt directories that have been added through the config.assets.excluded_paths. This is useful if you're for example using app/assets/stylesheets exclusively as a set of inputs to a compiler like Dart Sass for Rails, and you don't want these input files to be part of the load path. (Remember you need to add full paths, like Rails.root.join("app/assets/stylesheets")).

These assets can be referenced through their logical path using the normal helpers like asset_path, image_tag, javascript_include_tag, and all the other asset helper tags. These logical references are automatically converted into digest-aware paths in production when assets:precompile has been run (through a JSON mapping file found in public/assets/.manifest.json).

Bypassing the digest step

If you need to put multiple files that refer to each other through Propshaft, like a JavaScript file and its source map, you have to digest these files in advance to retain stable file names. Propshaft looks for the specific pattern of -[digest].digested.js as the postfix to any asset file as an indication that the file has already been digested.

Improving performance in development

Before every request Propshaft checks if any asset was updated to decide if a cache sweep is needed. This verification is done using the application's configured file watcher which, by default, is ActiveSupport::FileUpdateChecker.

If you have a lot of assets in your project, you can improve performance by adding the listen gem to the development group in your Gemfile, and this line to the development.rb environment file:

config.file_watcher = ActiveSupport::EventedFileUpdateChecker

Migrating from Sprockets

Propshaft does a lot less than Sprockets, by design, so it might well be a fair bit of work to migrate if it's even desirable. This is particularly true if you rely on Sprockets to provide any form of transpiling, like CoffeeScript or Sass, or if you rely on any gems that do. You'll need to either stop transpiling or use a Node-based transpiler, like those in jsbundling-rails and cssbundling-rails.

On the other hand, if you're already bundling JavaScript and CSS through a Node-based setup, then Propshaft is going to slot in easily. Since you don't need another tool to bundle or transpile. Just to digest and serve.

But for greenfield apps using the default import-map approach, Propshaft can also work well, if you're able to deal with vanilla CSS.

Will Propshaft replace Sprockets as the Rails default?

Most likely, but Sprockets needs to be supported as well for a long time to come. Plenty of apps and gems were built on Sprocket features, and they won't be migrating soon. Still working out the compatibility story. This is very much beta software at the moment.

License

Propshaft is released under the MIT License.

propshaft's People

Contributors

aaronjensen avatar brenogazzola avatar byroot avatar codergeek121 avatar dhh avatar dunglas avatar esasse avatar hashnotadam avatar kevinmcconnell avatar kevynlebouille avatar m-nakamura145 avatar markstanley-nps avatar ntl avatar ocarreterom avatar olleolleolle avatar paul-millar avatar pond avatar rafaelfranca avatar rainerborene avatar rience avatar rjaus avatar rmacklin avatar sedubois avatar skatkov avatar stevegeek avatar tagcincy avatar theodorton avatar thibaudgg avatar thomascchen avatar wlipa avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

propshaft's Issues

config.asset_host as a proc breaks asset paths

I have a multi-tenant app that dynamically sets a CDN for each domain hosted. I use a proc in the config like this:

config.asset_host = -> (asset, request) { "https://cdn.#{request.domain}" }

This way domain1.com, domain2.com, domain3.com all have their own CDN at cdn.domain1.com, cdn.domain2.com, cdn.domain3.com... etc. All derived from the request. The stylesheet tags all look like:

<link rel="stylesheet" href="https://cdn.example1.com/assets/fonts-cdf664222a5fa20e6165a5732e3560a3bedbcc8d.css">
<link rel="stylesheet" href="https://cdn.example2.com/assets/fonts-cdf664222a5fa20e6165a5732e3560a3bedbcc8d.css">
<link rel="stylesheet" href="https://cdn.example3.com/assets/fonts-cdf664222a5fa20e6165a5732e3560a3bedbcc8d.css">

With propshaft 0.7.0 the asset urls inside the css don't resolve correctly probably because propshaft isn't expecting a proc in the config, just a string.

With 0.7.0 the asset url generated looks like:

@font-face {
  src: url("#<Proc:0x00007f19f925a480 /app/project/releases/20230305134308/config/environments/production.rb:42 (lambda)>/assets/fonts/some-font-90c75952b33cc3eacf616e676327865cdb140281.woff2") format("woff2");
}

With previous versions of propshaft the asset url generated looks like:

@font-face {
  src: url("/assets/fonts/some-font-90c75952b33cc3eacf616e676327865cdb140281.woff2") format("woff2");
}

Maybe there is a better way for a multi tenant app to set various CDNs than setting the asset_host with a proc. ๐Ÿคทโ€โ™‚๏ธ

change is not detected in @import css file

If I understood correctly the idea of Portshaft (pls correct me if I'm wrong ) then the way how to include css files would be:

in app/assets/application.css

/* Application styles */
@import url('/bootstrap.css');
@import url('/my-custom.css');

in app/assets/bootstrap.css I have the content of bootstrap.css from Bootstrap 5 download page

in app/assets/my-custom.css:

p {
  color: red;
}

my app/views/layout/application.css

<!DOCTYPE html>
<html>
  <head>
    <title>Myapp</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>
  </head>

  <body>
    <h1>Propshaft test</h1>

    <div class="text-primary">
      If this text is blue, Bootstrap5 css works via Portshaft
    </div>

    <p>If this text is in color then @include css custom file works</p>
  </body>
</html>

So far so good, I run rails server and load the localhost:3000

Screenshot 2022-04-29 at 15 22 55

you can see the <p> is in red

Screenshot 2022-04-29 at 15 24 26

Now I change the app/assets/my-custom.css:

p {
  color: yellow;
}

I save the file and reload my localhost:3000

Screenshot 2022-04-29 at 15 25 26

Screenshot 2022-04-29 at 15 26 01

As you can see the change that I want the collor of <p> to be yellot was not picked up.

Restarting of rails server don't help, only thing that helps is if I rename the file e.g. from app/assets/my-custom.css to app/assets/my-custom-xx.cssand alter the app/assets/application.css`

/* Application styles */
@import url('/bootstrap.css');
@import url('/my-custom-xx.css');

now it the

is yellow

Screenshot 2022-04-29 at 15 34 17

  • this is fresh Rails 7.0.2.4 & Ruby 3.1.1 project generated with rails new myapp -a propshaft
  • no I didn't run rails assets:precompile
rails   assets:reveal
bootstrap.css
application.css
my-custom-xx.css
my-custom.css
stimulus.min.js
stimulus.js
stimulus.min.js.map
stimulus-importmap-autoloader.js
stimulus-autoloader.js
stimulus-loading.js
turbo.min.js.map
turbo.js
turbo.min.js
es-module-shims.js.map
es-module-shims.js
es-module-shims.min.js
actiontext.js
trix.js
trix.css
action_cable.js
actioncable.esm.js
actioncable.js
activestorage.esm.js
activestorage.js
rails-ujs.js
application.js
controllers/hello_controller.js
controllers/index.js
controllers/application.js

Proposal for capturing digested assets path

Hey there! I would like to share some bits of code that we implemented at our company and it could be merged into the official propshaft gem. Right now there is no way to reference digested assets from webpack, rollup.js or any other build system. On the other side, propshaft already does recognize digested assets with .digested. pattern on the filename. The motivation for this feature is to be able to use preload_link_tag or reference any kind of asset generated from external builders. No more talk, here is the code.

# lib/propshaft/digested.rb
module Propshaft
  CACHE = {}

  module Digested
    refine Propshaft::LoadPath do
      def resolve_digested(asset_name)
        Propshaft.resolve_digested_from(asset_name, source: assets_by_path)
      end
    end

    refine Propshaft::Resolver::Static do
      def resolve_digested(asset_name)
        Propshaft.resolve_digested_from(asset_name, source: parsed_manifest)
      end
    end

    refine Propshaft::Resolver::Dynamic do
      def resolve_digested(asset_name)
        load_path.resolve_digested(asset_name)
      end
    end
  end

  def self.resolve_digested_from(asset_name, source:)
    CACHE[asset_name] ||= source.find do |logical_path, _|
      filename, extname = asset_name.split(".")
      pattern = /\A#{filename}-([0-9a-zA-Z]{7,128})\.digested\.#{extname}\z/
      pattern.match?(logical_path)
    end&.first
  end
end

# app/helpers/propshaft_helper.rb
module PropshaftHelper
  using Propshaft::Digested

  def digested_path(path)
    Rails.application.assets.resolver.resolve_digested(path) || raise(Propshaft::MissingAssetError.new(path))
  end
end

Let me know if this sounds good so I can start working on a pull request. :D

Propshaft appears to be ignoring Rails config.relative_url_root

I have just switched to use propshaft from sprockets and it has all gone well except for one last issue.

When running in a local development environment, with the rails config.relative_url_root set, the compilers do not prepend this value onto the generated urls.

url([asset]) becomes url([config.assets.prefix]/[digested-asset]) not url([config.relative_url_root]/[config.assets.prefix]/[digested-file])

This means the assets cannot be found as they are under the url root.

I believe in production I can overcome this by setting the config.assets.prefix to be <config.relative_url_root>/assets as this will generate the correct path, and then the assets are served up separately.

However, the above solution does not work in development (or test) as while the correct url is generated in the css, the asset cannot be served by the development server (they are under /url_root/url_root/assets)

Is there a way of setting up the configuration so that I can use relative_url_root in development or should the compilers be including the relative_url_root in the generated path

Blocking Sprockets seeping in from gem dependencies

I'm trying to bundle add solidus && rails generate solidus:install but it carries with it Sprockets which will automatically replace my app's Propshaft so that everything gets messed up. Is there a way to force blockage of Sprockets somehow?

Only the result of Dart sass build should be propshafted

Hi,

I am using dartsass-rails gem, it add a task before asset:precompile run, that task output a unique file application.css (for the simpliest case) in assets/builds.

Should not propshaft had only this files and copy it in public/assets folder ?

when a url path has query parameters, the url rewriting does not add the digest (case bootstrap icons)

recent versions of bootstrap icons add a hash parameter in the resource reference, e.g. ./fonts/bootstrap-icons.woff2?8d200481aa7f02a2d63a331fc782cfaf

because of the extra query param (which should not really matter) propshaft seems unable to match it with the actual file even though assets:reveal shows it finds the linked file.

propshaft should probably remove query parameters from local urls before matching it with the exposed assets it's serving for doing it's digest rewriting

(EDITED) this issue seems difficult to reproduce. at first propshaft was not digesting correctly. and when we removed the query parameter, propshaft started doing the digesting correctly. however, once we added the query param (hash) again to the font url, propshaft kept doing the digesting correctly. we're currently kind of seriously puzzled on this and what caused propshaft to not do the digesting correctly initially.

404 response on assets in production

Hello,

I've migrated from sprockets to propshaft with cssbundling and jsbundling. While everything works fine in development mode it doesn't in production.

When running rake assets:reveal I see all required assets properly listed in the output and assets:precompile generates all required assets including digests in the public/assets folder. e.g: application-15fae71d9b66770b18316ce2e449486d37028831.css etc. However; when opening up the production app the app is unable to locate the generated assets properly. I'm getting 404 responses in the browser.

Request URL: http://localhost:3000/assets/application-15fae71d9b66770b18316ce2e449486d37028831.css
Request Method: GET
Status Code: 404 Not Found
Remote Address: 127.0.0.1:3000
Referrer Policy: strict-origin-when-cross-origin

File digests seem to be correspond to the ones in manifest.json and the actual file names so that shouldn't be it.

Is there anything I'm missing?

Tracking changes on multiple files, not only application.css

Houston we have a problem). Hi guys, I have trouble with using many CSS files in the folder app/assets/stylesheets, like when I add a file like buttons.css and load it to the application.css by @import url('buttons.css'); that is done, but.. when I make changes inside file buttons.css - no changes on-site, I mean that changes, not tracks or maybe I do something wrong. Can you help me please with it?

Support for .erb files

In sprockets if you place assets/javascript/client/pay.html.erb and assets/javascript/client/pay.js.erb both will be compiled and placed in assets folder as client/pay.html and client/pay.js . Is there a way to replicate this behaviour in propshaft?

Avoid logging on precompile

Hi there.

While migrating to this awesome gem to handle the asset pipeline, I found that it logs every digested asset with this line: "Writing #{asset.digested_path}" from lib/propshaft/processor.rb in line 43.

My question is: Is there a way to avoid logging? I'm using Capistrano for deploy, if that is worth mentioning.

Thank you very much in advance.

svg inline display issues

I'm struggling to display svg images inline.

rails  (7.0.4)
propshaft (0.6.4)
cssbundling-rails (1.1.1)
jsbundling-rails (1.0.3)

Using webpack, propshaft.

The method proposed in the migration guide is not resulting in the SVG rendering.

Rails.application.assets.load_path.find('logo.svg').content

For example:

<div class="shape shape-bottom shape-fluid-x svg-shim text-gray-200">
  <%= Rails.application.assets.load_path.find('shapes/curves/curve-1.svg').content %>
</div>

results in:

<div class="shape shape-bottom shape-fluid-x svg-shim text-gray-200">
  &lt;svg viewBox="0 0 2880 48" fill="none" xmlns="http://www.w3.org/2000/svg"&gt;&lt;path d="M0 48h2880V0h-720C1442.5 52 720 0 720 0H0v48z"/&gt;&lt;/svg&gt;
</div>

Creating the image using the path outputs the SVG as expected.

<%= image_tag image_url('shapes/curves/curve-1.svg') %>

And putting the SVG directly into the template works.

<div class="shape shape-bottom shape-fluid-x svg-shim">
    <svg viewBox="0 0 2880 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M0 48h2880V0h-720C1442.5 52 720 0 720 0H0v48z" fill="currentColor"/></svg>
</div>

Propshaft ESM Modules chunks not resolved correctly (works with sprockets-rails)

application.js:
Screenshot 2021-12-10 at 16 41 36

app/views/layouts/application.html.erb:
Screenshot 2021-12-10 at 16 46 20

Google Chrome console / networks tab
Screenshot 2021-12-10 at 16 42 55
Screenshot 2021-12-10 at 16 42 45

Scenarios:

Gemfile: Propshaft uncommented, sprockets-rails commented
ESBUILD_MODULES=1 ./bin/dev
Output: ๐Ÿ”ด

Gemfile: Propshaft commented, sprockets-rails uncommented
ESBUILD_MODULES=1 ./bin/dev
Output: ๐ŸŸข

Gemfile: Propshaft commented, sprockets-rails uncommented
./bin/dev
Output: ๐ŸŸข

Gemfile: Propshaft uncommented, sprockets-rails commented
./bin/dev
Output: ๐ŸŸข

Minimalist application to reproduce the issue: https://github.com/navidemad/PropshaftApp

Configuration to replace standard url() calls with digested paths

This seems like a great solution to cssbundling-rails#22 and a replacement for Sprockets. However it would be ideal to have the ability to just replace all url() calls with the digested url even if it's behind a configuration flag.

The use cases for this are things like icon fonts, UI libraries, purchased themes all containing CSS that reference assets that come along with them such as images, font files etc.. A conscience effort has to be made (even with Sprockets) to go through and edit the supplied files to use the asset-path helpers. Having the ability to replace all url() calls would allow drop in updates to these types of assets just by dropping the files into folders within config.assets.paths.

Happy to work on a PR if anyone else agrees this is desired.

Sorting paths messes up tailwind

Ever since #71 or its fix in #72 the order of paths gets scrambled and that messes up tailwind for me.

I don't do any config.assets modification at all so this should all be default. I have the latest version of all the other gems.

Propshaft::LoadPath#initialize

propshaft 0.6.1 logic:

[4] pry(#<Propshaft::LoadPath>)> Array(paths).collect { |path| Pathname.new(path) }
=> [#<Pathname:/Users/miharekar/Development/Personal/decent-visualizer/app/assets/builds>,
 #<Pathname:/Users/miharekar/Development/Personal/decent-visualizer/app/assets/images>,
 #<Pathname:/Users/miharekar/Development/Personal/decent-visualizer/app/assets/stylesheets>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/turbo-rails-1.0.1/app/assets/javascripts>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/tailwindcss-rails-2.0.6-arm64-darwin/app/assets/fonts>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/tailwindcss-rails-2.0.6-arm64-darwin/app/assets/stylesheets>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/stimulus-rails-1.0.3/app/assets/javascripts>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/importmap-rails-1.0.3/app/assets/javascripts>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/actiontext-7.0.2.2/app/assets/javascripts>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/actiontext-7.0.2.2/app/assets/stylesheets>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/actioncable-7.0.2.2/app/assets/javascripts>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/activestorage-7.0.2.2/app/assets/javascripts>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/actionview-7.0.2.2/lib/assets/compiled>,
 #<Pathname:/Users/miharekar/Development/Personal/decent-visualizer/app/javascript>,
 #<Pathname:/Users/miharekar/Development/Personal/decent-visualizer/vendor/javascript>]

propshaft 0.6.3 logic:

[5] pry(#<Propshaft::LoadPath>)> dedup(paths)
=> [#<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/actioncable-7.0.2.2/app/assets/javascripts>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/actiontext-7.0.2.2/app/assets/javascripts>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/actiontext-7.0.2.2/app/assets/stylesheets>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/actionview-7.0.2.2/lib/assets/compiled>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/activestorage-7.0.2.2/app/assets/javascripts>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/importmap-rails-1.0.3/app/assets/javascripts>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/stimulus-rails-1.0.3/app/assets/javascripts>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/tailwindcss-rails-2.0.6-arm64-darwin/app/assets/fonts>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/tailwindcss-rails-2.0.6-arm64-darwin/app/assets/stylesheets>,
 #<Pathname:/Users/miharekar/.gem/ruby/3.1.1/gems/turbo-rails-1.0.1/app/assets/javascripts>,
 #<Pathname:/Users/miharekar/Development/Personal/decent-visualizer/app/assets/builds>,
 #<Pathname:/Users/miharekar/Development/Personal/decent-visualizer/app/assets/images>,
 #<Pathname:/Users/miharekar/Development/Personal/decent-visualizer/app/assets/stylesheets>,
 #<Pathname:/Users/miharekar/Development/Personal/decent-visualizer/app/javascript>,
 #<Pathname:/Users/miharekar/Development/Personal/decent-visualizer/vendor/javascript>]
Before After
CleanShot 2022-02-21 at 12 37 35@2x CleanShot 2022-02-21 at 12 37 59@2x

So in the new version the tailwind compilation doesn't seem to work but I don't know enough about it to dive deeper.

Doing @paths = dedup(paths).sort.reverse fixes it, so this is definitely the issue, but that's not a proper fix ๐Ÿ˜…

I think the dedup method should respect the order of paths passed in.

Large numbers of files in my app/assets directory causes development to become very slow

I was using Sprockets and decided to check Propshaft out since I don't use any of the unique features of Sprockets. I have a copy of all of the Font Awesome SVGs in app/assets/svgs.

With Sprockets a request in development is instantaneous, whereas with Propshaft every request takes a second or more.

When I removed most of the icons from the asset directory Rails+Propshaft became fast.

Upgrade documentation typo on Section 3

Hi! I was just following the sprockets upgrade guide and spotted a typo:

On Section 3, step 1, it says Replace 'sass-rails' with 'propshaft'; - guess this should be Replace 'sprockets-rails' with 'propshaft';?

Also, in case its useful for future docs:

while this guide is clearly about Sprockets -> propshaft, I was moving from a webpacker with no sprockets-rails setup, in which case if you first remove webpacker (Steps 1/2 on Section 1) it is not possible to run the jsbundling-rails install script on Step 3 as Rails will fail to boot with NoMethodError: undefined method 'assets' for #<Rails::Application::Configuration if the environment contains any assets config. So need to do the install before removing webpacker or add the sprockets railtie temporarily.

Just finished migrating a large app to js/cssbundling-rails & propshaft ๐ŸŽ‰ Thanks!

Flag Icons not showing

Hi,

I wish to use https://github.com/lipis/flag-icons in my app. In my main .scss file I'm doing @import "flag-icons/sass/flag-icons"; but the flag images are not outputting.

From looking at the library, the file paths are being output like so:

@mixin flag-icon($country) {
  .fi-#{$country} {
    background-image: url(#{$flag-icons-path}#{$flag-icons-rect-path}/#{$country}.svg);

    @if $flag-icons-use-square {
      &.fis {
        background-image: url(#{$flag-icons-path}#{$flag-icons-square-path}/#{$country}.svg);
      }
    }
  }
}

Which results in compiled code of:

.fi-de {
  background-image: url(../flags/4x3/de.svg);
}

Any ideas why this is not working?

Thanks,
Neil

Newly added files that are already digested aren't available in development

Hi, we have a couple of assets that we don't want propshaft to digest and therefore we add the predigested suffix to its file name. This worked until the new version 0.7.0. This pull request introduced changes that broke this feature. It doesn't add new assets with the digested suffix the load_path (only the first found asset).
The problem is that in this line asset.logical_path is always filename.js and not filename-xyz.digested.js. This is because in this line it removes the predigested part from the logical path.

Upgrade doc for Rails 7 without webpacker

Hi,

I've recently started a brand new Rails 7 application with Tailwind and no webpacker.

I can see that sprocket-rails is in my Gemfile. In the Gemfile.lock there is no bundling gem and nothing regarding Webpack or Webpacker.

What should we do in a case like this?
Is using Propshaft instead of Sprockets even recommended?

Thanks for all the great work ๐Ÿ‘

customizing css lib (e.g. Bootstrap)

with Sprockets if if a developer would use scss file of bootstrap (E.g gem twbs/bootstrap) he could change configuration with scss variables like:

// in app/assets/stylesheets/application.scss
$primary: #c11;
@import "bootstrap";

Question 1: Is it save to assume this will not be possible with Propshaft ? Even with Bootstrap css variables ?

I'm asking this just to be 100% sure I'm not missing something. I fully understand Propshaft is not precompiler of scss like Sprockets/Webpacker

Question 2: if so, how would developer approach this customisation ?

Is it save to assume the choices are:

  1. esbuild or webpacker, ... (but he we would be back to square 1 => the need of node)
  2. dartsass-rails (which is good, but then where would be the line what css files place to Propshaft and which to dartsass assets)
  3. download the already costumized bootstrap.css with some tool like https://bootstrap.build/app and load that with propshaft

anything else I've missed?

(personally I prefer option 3)

Raising an error when an asset is not found

As per the Rails docs, if one is using sprockets-rails >= 3.2.0 it's possible to set config.assets.unknown_asset_fallback = false and raise an error when an asset is not found. Looking at the current implementation of asset_url we get a warning if propshaft is unable to resolve an asset URL. Would it be possible to have a similar configuration that would allow raising an error instead (especially on assets:precompile)?

propshaft with importmap-rails get js file 404s

Hi all,
I've recently moved to propshaft - it works well for my CSS, but I've got an issue where it seems the importmap doesn't play nicely with js file digests from propshaft?
Here's my layout with css/js include tags

// app/views/layouts/application.html.erb
...
    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>
...

The importmap_tags render to the following

//application.html
   <script type="importmap" data-turbo-track="reload">{
  "imports": {
    "application": "/assets/application-21d758878bc6e580bde82acbd80fe95b7de35812.js",
    "@hotwired/turbo-rails": "/assets/turbo-e40a6f56b17550917ebb699ac84022e5715c8b05.js",
    "nouislider": "https://cdn.skypack.dev/nouislider"
  }
}</script>
<link rel="modulepreload" href="/assets/application-21d758878bc6e580bde82acbd80fe95b7de35812.js">
<link rel="modulepreload" href="/assets/turbo-e40a6f56b17550917ebb699ac84022e5715c8b05.js">
<script src="/assets/es-module-shims.min-e2b12bbbb10c875738c2e170931855bd187b4b90.js" async="async" data-turbo-track="reload"></script>
<script type="module">import "application"</script>

here's my application.js that imports all the required files

// application.js
import { Turbo } from  "@hotwired/turbo-rails"
import { DataSourceValidator } from './src/data_source_validator.js';
import { FormFiller } from './src/form_filler.js';

All the local file imports in application.js 404 at the path /assets/src/
image

Is this because the src files are all digested by propshaft but the module importer is trying to import the plain filenames?
Has anyone got any recommendations for fixing this please?

undefined method `digested_path'

I'm testing an app with

  • rails:master (25418676cd36d5b4f87a55178f53b80f2fdcf900)
  • cssbundling-rails (0.2.4)
  • jsbundling-rails (0.1.9)
  • propshaft (0.2.0)

and I'm getting

| Started GET "/assets/application-65a777c93a558c83762b4590df80548504aea999.css" for ::1 at 2021-11-04 14:44:49 -0300
|
| NoMethodError (undefined method `digested_path' for nil:NilClass):
|
| propshaft (0.2.0) lib/propshaft/compilers/css_asset_urls.rb:28:in `asset_url'

Is this supposed to work? Am I missing something? Any incompabilities?

Migrate from Sprockets 4

As stuck with issue reported here, started to migrate Sprockets 4 to Propshaft

Used:

rails tmp:clear
rake assets:clobber
rake assets:precompile
ruby-3.1.2

gem 'rails', '7.0.3.1'
gem 'webrick'
gem 'propshaft', github: 'rails/propshaft', branch: 'main'
gem 'dartsass-rails', '~> 0.4.0'
...

Starting server with:
./bin/dev

Opening web page gives:

The asset 'print.css' was not found in the load path.

= stylesheet_link_tag 'print', media: 'print'
app/views/layouts/login.html.haml content:
...
    = stylesheet_link_tag 'application', media: 'all'
    = stylesheet_link_tag 'print', media: 'print'

    = javascript_include_tag 'application'
...

Full trace gist

app/stylesheets/print.scss after precompile:
with sprockets4:
public/assets/print.css

With propshaft it is:
public/assets/print.scss

Content of app/assets/stylesheets/print.scss:

@use "print/articles" as print_articles;
@use "print/base" as print_base;

Renaming print.scss to print.css, problem is gone. Why propshaft not doing that?

How to refer to a font asset?

I've been trying out the new asset pipeline today:

  • Rails 7 rails/rails@63d7178
  • propshaft 0.4.1
  • cssbundling-rails 0.2.6 configured for Bulma
  • (importmap-rails 0.9.1)

It's so fast compared to webpack :)

However I can't figure out how to refer to a font asset in a stylesheet.

Here's the directory structure:

app/assets/
โ”œโ”€โ”€ builds/
โ”‚ย ย  โ””โ”€โ”€ application.css
โ”œโ”€โ”€ config/
โ”œโ”€โ”€ fonts/
โ”‚ย ย  โ””โ”€โ”€ susa-regular.woff2
โ”œโ”€โ”€ images/
โ””โ”€โ”€ stylesheets/
    โ”œโ”€โ”€ application.bulma.sass
    โ””โ”€โ”€ fonts.sass

And here are the test stylesheets:

// app/assets/stylesheets/application.bulma.sass
@import 'fonts'
// app/assets/stylesheets/fonts.sass
@font-face
  font-family: 'SusaRegular'
  src: url('../fonts/susa-regular.woff2') format('woff2')

.logotype
  font-family: 'SusaRegular', serif
<!-- html snippet ->
<p class="logotype">hello</p>

The application.css which is built by propshaft and/or cssbundling repeats the font-face's src declaration verbatim. I had hoped it would replace it with a path to a digested copy of the font file.

So, unsurprisingly, when I load a page in the browser, there's a 404 for GET http://0.0.0.0:3000/fonts/susa-regular.woff2.

I checked Rails.application.config.assets.paths in the Rails console; it includes my app/assets/fonts/ directory.

I tried using asset-path(...) instead of url(...), but it didn't work (though I didn't expect to as I think it's obsolete?).

Any tips would be much appreciated. Thank you!

bin/rails routes: Propshaft::Server emits a lot of noise

This is more of a "polish" kind of issue, but bin/rails routes inspects Propshaft::Server when it prints out the /assets entry:

rails-routes-propshaft-example

If Propshaft::Server is a singleton, perhaps the output should just be Propshaft::Server, which would look like this:

           Prefix Verb URI Pattern        Controller#Action
                       /assets            Propshaft::Server
          widgets GET  /widgets(.:format) widgets#index

I'd be happy to submit a PR, but I'm not sure whether implementing #inspect on Propshaft::Server is an ideal solution in this case, since #inspect is useful in many other scenarios besides the Rails route inspector.

Here is the relevant code in the route inspector: #37

Side note: Engines/Railties don't appear to have this problem, since the route inspector ends up calling inspect on the classes, rather than instances.

Using images inside node_modules

Context:

Using Propshaft, cssbundling-rails, jsbundling-rails, esbuild

I'm using a npm plugin that has a css like this:

// node_modules/the_module/build/css/styles.css

.some-div {
  background: url('../img/icon.png')
}

the background refers to

node_modules/the_module/build/img/icon.png

In my application.scss I have

@import 'the_module/build/css/styles';

The css file gets included in my precompiled application.css but it tries to find an image in localhost:3000/img/icon.png which of course does not exist.

How can I make that image available under assets?

Asset digest is computed before compilation

This is the underlying cause of #90 but the description and comments there conflate a number of issues.

Steps to reproduce

Create two CSS files:

/* application.css */
import('other.css');
/* other.css */
body {
  background-color: pink;
}

Load application.css:

<%= stylesheet_link_tag 'application' %>

Load the page (with browser caching enabled) and observe a pink background.

Change the body background in other.css:

-  background-color: pink;
+  background-color: red;

Reload the page.

Expected result

The background is now red.

Actual result

The background is still pink.

Why this happens

The digest for application.css is computed from the unprocessed file content. This causes Propshaft::Asset to produce the same digest for the asset even though the actual contents at that URL will be different (due to the changing digest of other.css).

Because the asset is served with a very long expiry, browsers will hang on to the outdated version of the asset for a very long time.

Why that's a problem

In a production scenario, browsers would never get the new version of other.css. This makes using @import a pretty big foot gun.

v0.4.2 appears to be broken

Upgrading from propshaft v0.4.1 to v0.4.2 introduced this error on application initialization:

undefined local variable or method `paths' for #<Propshaft::Railtie:0x00005cd9e1dc97c8> (NameError)

from this line:

app.config.assets.paths.unshift(*paths["vendor/assets"].existent_directories)

Full stack trace from trying to load a rails console
$ bundle exec rails c
Traceback (most recent call last):
        37: from bin/rails:4:in `<main>'
        36: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/bootsnap-1.9.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
        35: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/bootsnap-1.9.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
        34: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/bootsnap-1.9.3/lib/bootsnap/load_path_cache/loaded_features_index.rb:100:in `register'
        33: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/bootsnap-1.9.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
        32: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/bootsnap-1.9.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
        31: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/bundler/gems/rails-14f190f33710/railties/lib/rails/commands.rb:18:in `<main>'
        30: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/bundler/gems/rails-14f190f33710/railties/lib/rails/command.rb:48:in `invoke'
        29: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/bundler/gems/rails-14f190f33710/railties/lib/rails/command/base.rb:87:in `perform'
        28: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/thor-1.1.0/lib/thor.rb:392:in `dispatch'
        27: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/thor-1.1.0/lib/thor/invocation.rb:127:in `invoke_command'
        26: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/thor-1.1.0/lib/thor/command.rb:27:in `run'
        25: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/bundler/gems/rails-14f190f33710/railties/lib/rails/commands/console/console_command.rb:101:in `perform'
        24: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/bundler/gems/rails-14f190f33710/railties/lib/rails/command/actions.rb:15:in `require_application_and_environment!'
        23: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/bundler/gems/rails-14f190f33710/railties/lib/rails/command/actions.rb:28:in `require_environment!'
        22: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/bundler/gems/rails-14f190f33710/railties/lib/rails/application.rb:345:in `require_environment!'
        21: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/zeitwerk-2.5.1/lib/zeitwerk/kernel.rb:35:in `require'
        20: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/bootsnap-1.9.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:31:in `require'
        19: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/bootsnap-1.9.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require_with_bootsnap_lfi'
        18: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/bootsnap-1.9.3/lib/bootsnap/load_path_cache/loaded_features_index.rb:100:in `register'
        17: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/bootsnap-1.9.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `block in require_with_bootsnap_lfi'
        16: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/bootsnap-1.9.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:23:in `require'
        15: from /home/richard/playground/rails-propshaft-example/config/environment.rb:5:in `<main>'
        14: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/bundler/gems/rails-14f190f33710/railties/lib/rails/application.rb:369:in `initialize!'
        13: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/bundler/gems/rails-14f190f33710/railties/lib/rails/initializable.rb:60:in `run_initializers'
        12: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/tsort.rb:205:in `tsort_each'
        11: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/tsort.rb:226:in `tsort_each'
        10: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/tsort.rb:347:in `each_strongly_connected_component'
         9: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/tsort.rb:347:in `call'
         8: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/tsort.rb:347:in `each'
         7: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/tsort.rb:349:in `block in each_strongly_connected_component'
         6: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/tsort.rb:431:in `each_strongly_connected_component_from'
         5: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
         4: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/2.7.0/tsort.rb:228:in `block in tsort_each'
         3: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/bundler/gems/rails-14f190f33710/railties/lib/rails/initializable.rb:61:in `block in run_initializers'
         2: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/bundler/gems/rails-14f190f33710/railties/lib/rails/initializable.rb:32:in `run'
         1: from /home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/bundler/gems/rails-14f190f33710/railties/lib/rails/initializable.rb:32:in `instance_exec'
/home/richard/.asdf/installs/ruby/2.7.2/lib/ruby/gems/2.7.0/gems/propshaft-0.4.2/lib/propshaft/railtie.rb:39:in `block in <class:Railtie>': undefined local variable or method `paths' for #<Propshaft::Railtie:0x00005cd9e1dc97c8> (NameError)
Did you mean?  Pathname

I added a test app for integration testing in #20, and it's producing the same error after rebasing:
https://github.com/rails/propshaft/runs/4414708235?check_suite_focus=true#step:4:4

66ce0f8 seems to be the commit that broke this. If I revert that commit, the integration test passes:
https://github.com/rmacklin/propshaft/runs/4414863639?check_suite_focus=true#step:4:10

Not able to detect changes in the assets

I switched from sprockets to propshaft assets compiler in my project. To make that switch, I followed the instructions from this document by Propshaft.

When using sprockets, I had to run rails assets:precompile every time I made any changes to a css, js, file or add a new image in the assets folder. To overcome this in Propshaft I followed the instructions in the documentation which says:

Propshaft uses a dynamic assets resolver in development mode. However, when you run assets:precompile locally Propshaft will then switch to a static assets resolver. Therefore, changes to assets will not be observed anymore and you will have to precompile the assets each time changes are made. This is different to Sprockets.
If you wish to have dynamic assets resolver enabled again, you need to clean your target folder (usually public/assets) and propshaft will start serving dynamic content from the source.

So I deleted all the files in the public/assets folder and re-initialized the rails server, but unfortunately, this didn't work & I do have to pre-compile assets in order to see the changes.

My application is running on rails 7.0.4 & ruby 3.1.2 and this is the command I gave to initialize the app:

rails new --skip-test -d=postgresql -j esbuild --css bootstrap fmb

So been using esbuild & bootstrap for JS & CSS respectively with sprockets as a assets compiler & to transition to propshaft, I:

  1. replaced the sprockets with propshaft gem.
  2. run bundle install.
  3. Didn't have config.assets.paths << Rails.root.join('app','assets'); in the config/application.rb file so no changes there.
  4. Deleted manifest.js file.
  5. Didn't have any image_url or font-url in my project so no changes there.

Could you let me know if I missed any steps here as everything does work perfectly except for the precompilation part? It's really annoying to compile the assets every time I make any changes to the assets files and re-initialize the server to see those changes.

Change "precompilation" to "compile"

I'll admin this is a bit pedantic, but since this project is so early, I'd like to make a case that assets are actually "compiled" before they're deployed.

"precompile" makes sense if a rails application was compiled, but since its interpreted, there's actually no compilation that happens.

Quite simply, assets are compiled before they are deployed along side a Rails application.

How do you switch Propshaft back to dynamic mode in Development?

The docs say:

Precompilation in development

Propshaft uses a dynamic assets resolver in development mode. However, when you run assets:precompile locally Propshaft will then switch to a static assets resolver. Therefore, changes to assets will not be observed anymore and you will have to precompile the assets each time changes are made. This is different to Sprockets.

My question is:
How do I switch it back? It seems crazy that if you run rails assets:precompile one time you're screwed... ๐Ÿ™

Propshaft Rails - Best practices on how to load multiple files. [question]

I'm playing around with Propshaft for couple of days now and I love it. I just want to be sure the way I'm using it is aligned with vision for Propshaft.

Question1 - is it ok to load multiple CSS via stylesheet_link_tag

Let say I need to load several vendor CSS files and several custom CSS files. I find it that the best way for me is to add multiple stylesheet_link_tag:

// app/views/layouts/application.css
<head>
    <%= stylesheet_link_tag "simplebar.min.css", "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag "leaflet.css", "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag "tiny-slider.css", "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag "nouislider.min.css", "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag "theme.min.css", "data-turbo-track": "reload" %> 

    <%= stylesheet_link_tag "trix", "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag "actiontext", "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag "rails_bootstrap_forms", "data-turbo-track": "reload" %>

    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag "my_other_custom.css", "data-turbo-track": "reload" %>
</head>

Is this considered ok/best-practice? Or would you suggest something else?

note I find it to import files from css with import url('/some-other.css') possible but problematic (more info here).

Question2 - load all CSS every-time or load particular context CSS ?

Hypothetically if my project ends up with 50 vendor CSS files and 17 css files. Is it ok to load all 67 at once in application layout (as described in Question1) or is it better to load them in different contexts. e.g.:

// app/views/layouts/application.css
<head>
    <!-- some core CSS -->
    <%= yield :head %>
    <%= stylesheet_link_tag "theme.min.css", "data-turbo-track": "reload" %> 
    <%= stylesheet_link_tag "rails_bootstrap_forms", "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
</head>
...
//app/views/posts/_form
....
<% content_for :head do %>
    <%= stylesheet_link_tag "trix", "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag "actiontext", "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag "my_other_custom.css", "data-turbo-track": "reload" %>

<% end %>

Although the second option makes theoretically more sense, especially for custom app css (no need to load marketing CSS everywhere), but when it comes to vendor CSS files is it worth the trouble ?

If I understand correctly all the CSS is cached in the browser = so it should not be a big deal. Any opinion on this ?

Question 3 - should developers try to group application CSS to few larger files or are lot of smaller files ok ?

which one you think is better for best practice:

Option A - Lot of smaller app files example :

// app/views/layouts/application.css
<head>
   ...
    <%= stylesheet_link_tag "frontpage.css", "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag "posts.css", "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag "publishing.css", "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag "comments.css", "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag "author.css", "data-turbo-track": "reload" %>
</head>

Option B - try to group them to one bigger CSS file:

// app/views/layouts/application.css
<head>
   ...
    <%= stylesheet_link_tag "frontpage.css", "data-turbo-track": "reload" %>
    <%= stylesheet_link_tag "blog.css", "data-turbo-track": "reload" %>
</head>

question 4 - is loading JavaScript via Propshaft bad idea?

yes ideally I should use importmaps to load my JS

BUT let say I'm working with one of bootstrap-themes (e.g.: Front). Once I download the theme I got bunch of vendor javascript files that I can just move to vendor/assets/javascript/ and load with javascript_include_tag

<body>
    ...
  
    <%= javascript_include_tag 'theme.min.js', defer: true %>
    <%= javascript_include_tag "tom-select.complete.min.js", defer: true %>
     ....
<body>

I find it really convenient and not colliding with my importmaps JS (Stimulus, Turbo, all works nice)

Is this considered OK ?

Thank you for your input

P.S. sorry I'm posting this question here, I tried to ask this on discuss.rubyonrails.org but spam filter marked my post as spam ๐Ÿ™„

asset prefix / path

With sprockets you were able to change the prefix in application.rb to something like config.assets.prefix = "/assetz" so you're able to use asset as a model/routes etc is there a need for this using propshaft? if so how would you go about changing this?

digested source maps

recently stumbled into this project while attempting a rails 7 update. I am using the predigested ".digested" method because my js files need to reference themselves, and am having 2 issues with the source maps.

  1. The sourceMappingURL is being striped away:
    Propshaft.logger.warn "Removed sourceMappingURL comment for missing asset '#{resolved_path}' from #{resolved_path}"
    (quick and dirty solution in dev mode is to call config.assets.compilers.pop to remove Propshaft::Compilers::SourceMappingUrls)
  2. Also the source maps will not be served without tweaking extract_path_and_digest - firien/propshaft@3903815...ed67507

Assets Not Precompiling in Dev Automatically

Hi All,

I've replaced Sprockets with Propshaft + Import Map combination and it worked great. However, there is one thing that is not working - when I change JS file, I need to run assets:precompile (and restart server) to see changes in dev environment.

I verified that on new Rails 7 project (created like this rails new myapp -a propshaft) and it's the same situation.

Is there a way to configure propshaft + importmap-rails to automatically handle this?

quiet_assets initializer breaks when using a custom Rails logger

We swap out the Rails::Rack::Logger middleware with a custom logger with app.middleware.swap(Rails::Rack::Logger, OurCustomLogger, logger_opts). When that happens, app.middleware.insert_before ::Rails::Rack::Logger, Propshaft::QuietAssets will throw a No such middleware to insert before: Rails::Rack::Logger RuntimeError.

app.middleware.insert_before ::Rails::Rack::Logger, Propshaft::QuietAssets

Clarify paths accepted by excluded_paths

Following c669e39 I set the following in my app:

config.assets.excluded_paths << 'app/assets/stylesheets'

But this didn't actually exclude my stylesheets directory. Looking at the source I realised I needed instead:

config.assets.excluded_paths << "#{Rails.root}/app/assets/stylesheets"

As an alternative I patched the source to automatically add the prefix:

diff --git i/lib/propshaft/railtie.rb w/lib/propshaft/railtie.rb
index a8236c6..b164e50 100644
--- i/lib/propshaft/railtie.rb
+++ w/lib/propshaft/railtie.rb
@@ -24,7 +24,9 @@ class Railtie < ::Rails::Railtie
       app.config.assets.paths.unshift(*paths["lib/assets"].existent_directories)
       app.config.assets.paths.unshift(*paths["app/assets"].existent_directories)
 
-      app.config.assets.paths = app.config.assets.paths.without(Array(app.config.assets.excluded_paths).collect(&:to_s))
+      app.config.assets.paths = app.config.assets.paths.without(
+        Array(app.config.assets.excluded_paths).collect { |p| paths.path.join(p) }.collect(&:to_s)
+      )
     end
 
     config.after_initialize do |app|

But then I realised this would exclude (in my case) app/assets/stylesheets from any asset path, not just my top-level assets.

Just to make it a bit clearer, would you be interested in a small PR like the following?

diff --git i/README.md w/README.md
index e5844e2..f0dca2b 100644
--- i/README.md
+++ w/README.md
@@ -18,7 +18,7 @@ With Rails 7+, you can start a new application with propshaft using `rails new m
 
 Propshaft makes all the assets from all the paths it's been configured with through `config.assets.paths` available for serving and will copy all of them into `public/assets` when precompiling. This is unlike Sprockets, which did not copy over assets that hadn't been explicitly included in one of the bundled assets. 
 
-You can however exempt directories that have been added through the `config.assets.excluded_paths`. This is useful if you're for example using `app/assets/stylesheets` exclusively as a set of inputs to a compiler like Dart Sass for Rails, and you don't want these input files to be part of the load path.
+You can however exempt directories that have been added through the `config.assets.excluded_paths`. This is useful if you're for example using `#{Rails.root}/app/assets/stylesheets` exclusively as a set of inputs to a compiler like Dart Sass for Rails, and you don't want these input files to be part of the load path.
 
 These assets can be referenced through their logical path using the normal helpers like `asset_path`, `image_tag`, `javascript_include_tag`, and all the other asset helper tags. These logical references are automatically converted into digest-aware paths in production when `assets:precompile` has been run (through a JSON mapping file found in `public/assets/.manifest.json`).
 

How to exclude assets from gems/engines? Known approach looks tedious or like a hack

I want to exclude all assets I am not using from precompiling.
I understand that config.assets.excluded_paths exists and that it requires a full path.

In my case Rails.application.config.assets.paths returns

["/usr/src/app/app/assets/builds",
 "/usr/src/app/app/assets/images",
 "/usr/local/bundle/gems/view_component-2.53.0/app/assets/vendor",
 "/usr/local/bundle/gems/unobtrusive_flash-3.3.1/lib/assets/javascripts",
 "/usr/local/bundle/gems/unobtrusive_flash-3.3.1/lib/assets/stylesheets",
 "/usr/local/bundle/gems/turbo-rails-1.0.1/app/assets/javascripts",
 "/usr/local/bundle/gems/nested_form-0.3.2/vendor/assets/javascripts",
 "/usr/local/bundle/gems/heroicon-0.4.0/app/assets/images",
 "/usr/local/bundle/gems/actioncable-7.0.2.3/app/assets/javascripts",
 "/usr/local/bundle/gems/actionview-7.0.2.3/lib/assets/compiled"]

I would like to exclude most of them, e.g. "/usr/local/bundle/gems/nested_form-0.3.2/vendor/assets/javascripts" or "/usr/local/bundle/gems/actionview-7.0.2.3/lib/assets/compiled".

I have two reasons for this: It takes comparatively long (e.g. assets from heroicon), I don't like to waste space (I know, it is not too much) and I like to have a clean directory with only the files I really use.

This is the only way I found to do so:

Rails.application.config.assets.excluded_paths << File.join(Heroicon.root, 'app/assets/images')
# rubocop:disable Style/StringConcatenation
Rails.application.config.assets.excluded_paths << (Pathname.new(Gem.find_files_from_load_path('nested_form').first) + '../../vendor/assets/javascripts')
Rails.application.config.assets.excluded_paths << (Pathname.new(Gem.find_files_from_load_path('turbo-rails').first) + '../../app/assets/javascripts')
Rails.application.config.assets.excluded_paths << (Pathname.new(Gem.find_files_from_load_path('unobtrusive_flash').first) + '../../lib/assets/stylesheets')
Rails.application.config.assets.excluded_paths << (Pathname.new(Gem.find_files_from_load_path('unobtrusive_flash').first) + '../../lib/assets/javascripts')
Rails.application.config.assets.excluded_paths << (Pathname.new(Gem.find_files_from_load_path('view_component').first) + '../../app/assets/vendor')
# rubocop:enable Style/StringConcatenation

Note: I am unable to use this methodology to exclude actionview or actioncable.

Pathname.new(Gem.find_files_from_load_path('whaever').first) does not look like a good solution

What is the recommended way?
How can this be simplified?

Propshaft race condition with webpack creates 410 with dynamic chunk loading

Hi, first of all, thanks a lot for developing this new solution. We are using it in production and are quite happy. One issue we bump into quite frequently though is in development.

We use webpack and depend on dynamic chunk loading. That means that webpack needs to compile these chunks already with a hash and a ".digested" suffix. We also use LiveReload from webpack to refresh the page after each compilation automatically.

What we observed is that when we apply multiple changes in succession, then a dynamic chunk request might return a 410. We debugged the code and could see that the asset wasn't in the @cached_assets_by_path. We also saw that propshaft clears the cache when any of the files in app/assets/builds changes. We suspect that there is some kind of race-condition between webpack and propshaft, clearing the cache, compiling multiple times and refreshing the browser at the same time.

We also noted that the digested assets created by webpack accumulate over time which could also slow down the creations of cached_assets_by_path. This accumulation happens also in production and doesn't get cleared by assets:clear.

We tried to find a reproducible example but since it is likely a race condition it is very hard. We would appreciate any thoughts and pointers.

Generating GZip and Brotli compressed versions of assets

In migrating from Sprockets to a Propshaft + JavaScript Bundling and CSS Bundling for Rails set up, we have lost the generation of .gz compressed versions of our JavaScript and CSS assets (see Sprockets' ZlibExporter).

As these files would automatically be served by the ActionDispatch::Static middleware (useful when serving Rails applications from Heroku), would adding similar functionality to Propshaft be welcome or is it out of scope for the project?

I'd imagine it would be an optional part of outputting the assets in the Processor, creating versions of each compressable asset with the same filename but with .gz and .br appended.

`CssAssetCompiler` is stripping anchors from SVG sprites

Expected

When I reference an SVG sprite member in a stylesheet, it preserves the anchor pointing that member. To illustrate:

.icon.icon-rails { background-image: url('icons.svg#rails'); }

Should output

.icon.icon-rails { background-image: url('/assets/icons-abcdef123456.svg#rails'); }

Actual

The CssAssetCompiler strips the anchor from the URL and results in no image being embedded:

.icon.icon-rails { background-image: url('icons.svg#rails'); }

Outputs:

.icon.icon-rails { background-image: url('/assets/icons-abcdef123456.svg'); }

Sample app

I created a simple example of the issue here: https://github.com/flipsasser/propshaft-svg-sprite-example

Changes to `assets.prefix` do not change hash

Theoretically, anything that changes the behavior of a compiler should impact the hash. Barring that, would it make sense to bring back config.assets.version and include that in the hash?

Importing libraries with fonts from node_modules (Bootstrap Icons example)

Hello!

I created a sample Rails 7 RC1 application with Propshaft and I wanted to use Bootstrap Icons from NPM package.

As you can see in the README of my sample app, I had to play a few tricks to make them work because the fonts import didn't work out-of-the-box.

I wanted some advice from multiple developers if there was a potential issue with Propshaft not handling the default font path correctly in bootstrap-icons and if there's not, if my workaround is good. I tried a few things and I don't know if it is the smartest solution.

This discussion may lead to write some documentation for best practices around Propshaft.

Thanks in advance for your help!

`assets:precompile` causes `Propshaft::MissingAssetError` (dev & prod)

Environment

  • Rails 7.0.2.3
  • Ruby ruby 3.1.1p18 (2022-02-18 revision 53f5fc4236) [arm64-darwin21]
  • Bundler 2.3.9
  • Propshaft 0.6.4

Problem
As far as I understand Propshaft has two ways to resolve assets: dynamically and statically. Dynamic is the preferred way for development and static the way for production as assets get precompiled to public/assets.

Now, I recently deployed my project to Heroku and noticed that it crashes immediately as I visit the URL. Upon checking logs I noticed the following error message:

Propshaft::MissingAssetError in Home#index
The asset 'tailwind.css' was not found in the load path.
...

I double checked on Heroku and confirmed that the public/assets folder was correctly created. I also took a look at the .manifest.json and found no problems. I tried to reproduce the problem locally by running rails assets:precompile and voilร : same error message and same behavior (i.e. public/assets and .manifest.json look fine).

I noticed that this problem isn't specific to tailwind.css as stated in the error message - it seems to be any asset really. Oddly enough, upon rails assets:clobber everything works fine again locally as Propshaft switches back to the dynamic resolver.

Please note, this is fairly fresh rails project (about a week old) and I have not changed the default config in development.rb, production.rb and application.rb - yet I am unable to get the project to work properly after a rails assets:precompile.

What am I missing?

(I've attached a full stack trace just in case)
stack_trace.log

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.