Class: Shakapacker::Manifest

Inherits:
Object
  • Object
show all
Defined in:
lib/shakapacker/manifest.rb,
sig/shakapacker/manifest.rbs

Overview

Manifest for looking up compiled asset paths

The manifest reads the manifest.json file produced by webpack/rspack during compilation and provides methods to look up the compiled (digested) paths for source files.

Defined Under Namespace

Classes: LoadResult, MissingEntryError

Instance Method Summary collapse

Constructor Details

#initialize(instance) ⇒ Manifest

Creates a new manifest instance

Parameters:



46
47
48
# File 'lib/shakapacker/manifest.rb', line 46

def initialize(instance)
  @instance = instance
end

Instance Method Details

#compileBoolean

Returns:

  • (Boolean)


136
137
138
# File 'lib/shakapacker/manifest.rb', line 136

def compile
  Shakapacker.logger.tagged("Shakapacker") { compiler.compile }
end

#compilerCompiler

Returns the compiler instance

Returns:



39
# File 'sig/shakapacker/manifest.rbs', line 39

def compiler: () -> Compiler

#compiling?Boolean

Returns:

  • (Boolean)


132
133
134
# File 'lib/shakapacker/manifest.rb', line 132

def compiling?
  config.compile? && !dev_server.running?
end

#configConfiguration

Returns the configuration object

Returns:



36
# File 'sig/shakapacker/manifest.rbs', line 36

def config: () -> Configuration

#dataHash[String, untyped]

Returns:

  • (Hash[String, untyped])


149
150
151
# File 'lib/shakapacker/manifest.rb', line 149

def data
  load_result.data
end

#dev_serverDevServer

Returns the dev server instance

Returns:



42
# File 'sig/shakapacker/manifest.rbs', line 42

def dev_server: () -> DevServer

#find(name) ⇒ String, ...

Parameters:

  • key (String)

Returns:

  • (String, Hash[String, untyped], nil)


153
154
155
156
157
158
159
160
# File 'lib/shakapacker/manifest.rb', line 153

def find(name)
  return nil unless data[name.to_s].present?

  return data[name.to_s] unless data[name.to_s].respond_to?(:dig)

  # Try to return src, if that fails, (ex. entrypoints object) return the whole object.
  data[name.to_s].dig("src") || data[name.to_s]
end

#full_pack_name(name, pack_type) ⇒ String

Parameters:

  • name (String)
  • pack_type (Symbol, nil)

Returns:

  • (String)


162
163
164
165
# File 'lib/shakapacker/manifest.rb', line 162

def full_pack_name(name, pack_type)
  return name unless File.extname(name.to_s).empty?
  "#{name}.#{manifest_type(pack_type)}"
end

#handle_missing_entry(name, pack_type) ⇒ void

This method returns an undefined value.

Parameters:

  • name (String)
  • pack_type (Hash[Symbol, untyped])

Raises:



167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/shakapacker/manifest.rb', line 167

def handle_missing_entry(name, pack_type)
  # @load_result is always set by the preceding data call (even in
  # non-cached mode), so we read it directly to avoid a second file read.
  result = @load_result

  # An empty data hash covers both a 0-byte / whitespace-only manifest file
  # and a missing manifest file. Either way the bundler has not produced
  # usable output yet, so we show a targeted "unavailable" message.
  if result.data.empty?
    raise Shakapacker::Manifest::MissingEntryError, manifest_unavailable_error(result)
  end

  raise Shakapacker::Manifest::MissingEntryError, missing_file_from_manifest_error(full_pack_name(name, pack_type[:type]), result.data)
end

#loadLoadResult

Returns:



182
183
184
185
186
187
188
189
190
# File 'lib/shakapacker/manifest.rb', line 182

def load
  if config.manifest_path.exist?
    contents = config.manifest_path.read
    parsed = contents.strip.empty? ? {} : JSON.parse(contents)
    LoadResult.new(data: parsed, manifest_existed: true)
  else
    LoadResult.new(data: {}, manifest_existed: false)
  end
end

#load_resultLoadResult

Returns:



140
141
142
143
144
145
146
147
# File 'lib/shakapacker/manifest.rb', line 140

def load_result
  if config.cache_manifest?
    @load_result ||= load
  else
    refresh
    @load_result
  end
end

#lookup(name, pack_type = {}) ⇒ String?

Looks up the compiled path for a given asset

Parameters:

  • name (String)
  • pack_type (Hash[Symbol, untyped]) (defaults to: {})

Returns:

  • (String, nil)


109
110
111
112
113
# File 'lib/shakapacker/manifest.rb', line 109

def lookup(name, pack_type = {})
  compile if compiling?

  find(full_pack_name(name, pack_type[:type]))
end

#lookup!(name, pack_type = {}) ⇒ String

Like lookup, but raises an error if not found

Parameters:

  • name (String)
  • pack_type (Hash[Symbol, untyped]) (defaults to: {})

Returns:

  • (String)


127
128
129
# File 'lib/shakapacker/manifest.rb', line 127

def lookup!(name, pack_type = {})
  lookup(name, pack_type) || handle_missing_entry(name, pack_type)
end

#lookup_pack_with_chunks(name, pack_type = {}) ⇒ Array[String]?

Looks up an entry point with all its chunks (split code)

Parameters:

  • name (String)
  • pack_type (Hash[Symbol, untyped]) (defaults to: {})

Returns:

  • (Array[String], nil)


73
74
75
76
77
78
79
80
81
# File 'lib/shakapacker/manifest.rb', line 73

def lookup_pack_with_chunks(name, pack_type = {})
  compile if compiling?

  manifest_pack_type = manifest_type(pack_type[:type])
  manifest_pack_name = manifest_name(name, manifest_pack_type)
  find("entrypoints")[manifest_pack_name]["assets"][manifest_pack_type]
rescue NoMethodError
  nil
end

#lookup_pack_with_chunks!(name, pack_type = {}) ⇒ Array[String]

Like lookup_pack_with_chunks, but raises an error if not found

Parameters:

  • name (String)
  • pack_type (Hash[Symbol, untyped]) (defaults to: {})

Returns:

  • (Array[String])


89
90
91
# File 'lib/shakapacker/manifest.rb', line 89

def lookup_pack_with_chunks!(name, pack_type = {})
  lookup_pack_with_chunks(name, pack_type) || handle_missing_entry(name, pack_type)
end

#manifest_name(name, pack_type) ⇒ String

The manifest_name method strips of the file extension of the name, because in the manifest hash the entrypoints are defined by their pack name without the extension. When the user provides a name with a file extension, we want to try to strip it off.

Parameters:

  • name (String)
  • manifest_type (String)

Returns:

  • (String)


195
196
197
# File 'lib/shakapacker/manifest.rb', line 195

def manifest_name(name, pack_type)
  name.chomp(".#{pack_type}")
end

#manifest_type(pack_type) ⇒ String

Parameters:

  • type (Symbol, nil)

Returns:

  • (String)


199
200
201
202
203
204
205
# File 'lib/shakapacker/manifest.rb', line 199

def manifest_type(pack_type)
  case pack_type
  when :javascript then "js"
  when :stylesheet then "css"
  else pack_type.to_s
  end
end

#manifest_unavailable_error(result) ⇒ String

Parameters:

Returns:

  • (String)


225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/shakapacker/manifest.rb', line 225

def manifest_unavailable_error(result)
  bundler_name = config.assets_bundler

  if result.manifest_existed
    <<~MSG
      Shakapacker manifest is empty. #{bundler_name} is likely still compiling.

      This typically happens when:
      1. You just started the dev server and it's still compiling
      2. The dev server crashed during startup
      3. #{bundler_name} compilation hasn't completed yet

      What to do:
      - Wait a few seconds and refresh the page
      - Check your terminal for #{bundler_name} build progress
      - Look for errors in the #{bundler_name} output

      Manifest path: #{config.manifest_path}
    MSG
  else
    <<~MSG
      Shakapacker manifest file not found. #{bundler_name} has not yet built assets.

      This typically happens when:
      1. You haven't started the dev server yet
      2. The compile process hasn't created the manifest file
      3. The manifest_path configuration is incorrect

      What to do:
      - Start the dev server: bin/shakapacker-dev-server
      - Or run a manual build: bin/shakapacker
      - Verify manifest_path in config/shakapacker.yml

      Expected manifest path: #{config.manifest_path}
    MSG
  end
end

#missing_file_from_manifest_error(bundle_name, manifest_data) ⇒ String

Parameters:

  • bundle_name (String)
  • manifest_data (Hash[String, untyped])

Returns:

  • (String)


207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/shakapacker/manifest.rb', line 207

def missing_file_from_manifest_error(bundle_name, manifest_data)
  bundler_name = config.assets_bundler
  <<~MSG
    Shakapacker can't find #{bundle_name} in #{config.manifest_path}. Possible causes:
    1. You forgot to install javascript packages or are running an incompatible javascript runtime version
    2. Your app has code with a non-standard extension (like a `.jsx` file) but the extension is not in the `extensions` config in `config/shakapacker.yml`
    3. You have set compile: false (see `config/shakapacker.yml`) for this environment
       (unless you are using the `bin/shakapacker -w` or the `bin/shakapacker-dev-server`, in which case maybe you aren't running the dev server in the background?)
    4. Your #{bundler_name} has not yet FINISHED running to reflect updates.
    5. You have misconfigured Shakapacker's `config/shakapacker.yml` file.
    6. Your #{bundler_name} configuration is not creating a manifest with the expected structure.
    7. Ensure the 'assets_bundler' in config/shakapacker.yml is set correctly (currently: #{bundler_name}).

    Your manifest contains:
    #{JSON.pretty_generate(manifest_data)}
  MSG
end

#refreshHash[String, untyped]

Reloads the manifest data from disk

Returns:

  • (Hash[String, untyped])


56
57
58
59
# File 'lib/shakapacker/manifest.rb', line 56

def refresh
  @load_result = load
  @load_result.data
end