Class: Abqari::AssetPipeline

Inherits:
Object
  • Object
show all
Defined in:
lib/abqari/asset_pipeline.rb

Constant Summary collapse

OUTPUT_SUBDIR =
'assets'
HASH_LENGTH =
10
BUNDLE_LOGICAL =
'css/bundle.css'
THEME_NAME_RE =

A theme name is a directory name under themes/ and nothing more. The strict pattern is load-bearing: the name is joined into filesystem paths in several places (here, ArtifactWriter, Renderer), so without it theme: ../something walks and publishes arbitrary directories. Same containment class the symlink guards enforce.

/\A[a-z0-9_-]+\z/.freeze
MINIFY_KINDS =

Extensions that get minified before fingerprinting (production only — dev keeps sources readable for devtools).

{ '.css' => :css, '.js' => :js }.freeze
NO_COMMON_THEMES =

Themes that bring their own structural CSS. Mirror of the _head.html.erb conditional that skips _common.css.

%w[bootstrap none].freeze

Instance Method Summary collapse

Constructor Details

#initialize(site) ⇒ AssetPipeline

Returns a new instance of AssetPipeline.



25
26
27
28
29
# File 'lib/abqari/asset_pipeline.rb', line 25

def initialize(site)
  @site = site
  @paths = {}
  @sources = {}
end

Instance Method Details

#path_for(name) ⇒ Object



61
62
63
# File 'lib/abqari/asset_pipeline.rb', line 61

def path_for(name)
  @paths[name] || raise(ArgumentError, "Unknown asset: #{name}. Add it to app/assets/ or themes/#{theme_name}/.")
end

#processObject



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/abqari/asset_pipeline.rb', line 31

def process
  validate_theme!

  # Processing order matters — later passes overwrite earlier ones
  # on logical-path conflict (same filename).
  #
  # Within a category (theme vs app/assets), engine first, site
  # second — so a site can override a theme file or a structural
  # CSS asset just by dropping its own version at the same path.
  #
  # Between categories, themes lose to app/assets (preserving the
  # original semantic: `_common.css` and `site.css` win over
  # theme-bundled CSS on conflict). Hence the order:
  #   1. engine theme   →  2. site theme
  #   3. engine assets  →  4. site assets
  #
  # A theme's `public/` subtree is NOT an asset source — it's the
  # verbatim passthrough tree ArtifactWriter#copy_public ships to
  # the site root (e.g. the vendored Bootstrap dist). Walking it
  # here used to fingerprint ~700 KB of dead, unreferenced copies
  # into `_site/assets/public/`.
  [engine_theme_dir, site_theme_dir].uniq.each do |dir|
    process_dir(dir, skip_public: true) if dir && Dir.exist?(dir)
  end
  [engine_app_assets, site_app_assets].uniq.each do |dir|
    process_dir(dir) if dir && Dir.exist?(dir)
  end
  build_css_bundle
end

#theme_nameObject



65
66
67
# File 'lib/abqari/asset_pipeline.rb', line 65

def theme_name
  @site.config['theme'] || SiteConfig::DEFAULTS['theme']
end

#validate_theme!Object

Fail fast — called at the top of Site#build, BEFORE the output dir is wiped, so a config typo aborts with one clear message instead of N per-page "Unknown asset: css/app.css" failures (or, for a traversal-shaped name, a walk of whatever the path resolves to).



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/abqari/asset_pipeline.rb', line 74

def validate_theme!
  name = theme_name.to_s
  return if name == 'none'

  unless name.match?(THEME_NAME_RE)
    raise PathSafe::Error,
          "invalid theme name #{name.inspect}: must match #{THEME_NAME_RE.inspect} " \
          "(lowercase letters, digits, '-', '_'), or 'none'"
  end

  unless Dir.exist?(File.join(@site.engine_root, 'themes', name)) ||
         Dir.exist?(File.join(@site.site_root, 'themes', name))
    raise "unknown theme #{name.inspect} — no themes/#{name}/ in the site or the engine. " \
          "Available: #{available_theme_names.join(', ')}, or 'none'"
  end

  return if theme_app_css_candidates.any? { |f| File.file?(f) }

  raise "theme #{name.inspect} has no css/app.css — looked in " \
        "themes/#{name}/css/ (site and engine) and app/assets/css/ (site). " \
        'Every theme needs one, even if empty; it anchors the CSS bundle.'
end