Как ссылаться на активы по URL-адресу в JS в rails 7 с помощью importmap и Sprockets?

В приложении мне нужно создавать аудиофайлы из JS-файла (я использую AudioContext API) примерно так:

      playAudio(url) {
  this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
  let data = await fetch(url).then(response => response.arrayBuffer());
  let buffer = await this.audioContext.decodeAudioData(data)
  const source = this.audioContext.createBufferSource()
  source.buffer = buffer
  source.connect(this.audioContext.destination)
  source.start()
}

Этот файл JS представляет собой контроллер Stimulus, загруженный в новое приложение Rails 7, которое использует importmap и Sprockets.

В среде разработки JS может угадать путь, поскольку Sprockets будет обслуживать активы с их каноническим именем (например, /assets/audio/file.wav). Однако в рабочей среде во время прекомпиляции ресурсов Sprockets добавляет хеш после имени файла, и доступ к файлу будет осуществляться только с таким именем, как /assets/audio/file-f11ef113f11ef113f113.wav.

Это имя файла не может быть жестко задано, так как оно зависит от предварительной компиляции (технически я, вероятно, мог бы жестко указать путь с хэшем, поскольку файл не будет часто меняться, но я не хочу ничего предполагать об этом хэше).

Этот файл упоминается в манифесте, который Sprockets создает во время предварительной компиляции, помимо других ресурсов в общей папке. С использованием Rails.application.assets_manifest.filesЯ могу получить доступ к данным манифеста и безопасно выполнить сопоставление.

Вот помощник, который я написал для этого:

        def audio_assets_json
    audio_assets = Rails.application.assets_manifest.files.select do |_key, file|
      file['logical_path'].start_with?('audio/')
    end

    JSON.pretty_generate(
      audio_assets.to_h { |_k, f| [f['logical_path'], asset_url(f['logical_path'])] }
    )
  end

Но мне нужно получить доступ к этим данным из JS-файла, а поскольку манифест также имеет хэш в имени файла, мой JS не может просто загрузить его.

Мое текущее решение состоит в том, чтобы включить его в макет моего приложения, и это отлично работает:

          <script>
      window.assets = <%= audio_assets_json %>
      window.asset_url = function(path) {
        let result = assets[path]
        return result ? result : `/assets/${path}`
      }
    </script>

Проблема с этим решением заключается в том, что хэш записывается в каждый HTML-ответ от сервера приложений, что неэффективно. Кроме того, помощник вызывается во время выполнения, что также неэффективно: он генерируется динамически во время выполнения, тогда как это должно выполняться статически во время сборки развертывания.

Моя первоначальная идея состояла в том, чтобы создать список в .js.erbфайл, сгенерированный Sprockets во время прекомпиляции. Поэтому я переименовал controllers/application.jsс участием controllers/application.js.erbи вызвал помощник таким образом:

      <% environment.context_class.instance_eval { include ApplicationHelper } %>
window.assets = <%= audio_assets_json %>

JS был правильно сгенерирован Sprockets, но каким-то образом importmapне мог его увидеть, и консоль JS показывает следующую ошибку:

      Unable to resolve specifier 'controllers/application' from http://localhost:3000/assets/controllers/index-2db729dddcc5b979110e98de4b6720f83f91a123172e87281d5a58410fc43806.js

Я попытался добавить эту строку в config/initializers/assets.rb:

      Sprockets.register_mime_type 'application/javascript', extensions: ['.js.erb']

Я попытался добавить эту строку в assets/manifest.js:

      //= link_tree ../../javascript .js.erb

Но ничего из этого не помогло.

Итак, мой вопрос: как я могу ссылаться на URL-адрес активов из JS, используя importmap и Sprockets статически?

0 ответов

Другие вопросы по тегам