Class: Sakusei::VueProcessor

Inherits:
Object
  • Object
show all
Defined in:
lib/sakusei/vue_processor.rb

Overview

Processes Vue components at build time using a single Node.js process per build. Requires Node.js with @vue/server-renderer, @vue/compiler-sfc, and vue@3 installed.

Constant Summary collapse

VUE_COMPONENT_PATTERN =

Attribute values may contain > (e.g. HTML in slot-like props), so we match quoted strings properly

/<vue-component((?:\s+[\w-]+=(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'))*)\s*(?:\/>|>(.*?)<\/vue-component>)/m
INSTALL_INSTRUCTIONS =
<<~MSG
  Vue components detected but dependencies not found.

  To use Vue components, install the required npm packages:

    npm install @vue/server-renderer @vue/compiler-sfc vue@3

  Or initialize a new package.json first:

    npm init -y
    npm install @vue/server-renderer @vue/compiler-sfc vue@3
MSG

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(content, base_dir, style_pack: nil) ⇒ VueProcessor

Returns a new instance of VueProcessor.



27
28
29
30
31
# File 'lib/sakusei/vue_processor.rb', line 27

def initialize(content, base_dir, style_pack: nil)
  @content = content
  @base_dir = base_dir
  @style_pack = style_pack
end

Class Method Details

.available?Boolean

Returns:

  • (Boolean)


65
66
67
# File 'lib/sakusei/vue_processor.rb', line 65

def self.available?
  system('which node > /dev/null 2>&1') && vue_renderer_installed?
end

.vue_renderer_installed?Boolean

Returns:

  • (Boolean)


90
91
92
93
# File 'lib/sakusei/vue_processor.rb', line 90

def self.vue_renderer_installed?
  check_cmd = "cd '#{Dir.pwd}' && node -e \"try { require('@vue/server-renderer'); require('@vue/compiler-sfc'); process.exit(0); } catch(e) { process.exit(1); }\" 2>/dev/null"
  system(check_cmd)
end

Instance Method Details

#processObject

Raises:



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
60
61
62
63
# File 'lib/sakusei/vue_processor.rb', line 33

def process
  return @content unless vue_components_present?

  ensure_style_pack_deps_installed
  raise Error, INSTALL_INSTRUCTIONS unless vue_renderer_available?

  jobs = []
  content_with_placeholders = first_pass(@content, jobs)
  return content_with_placeholders if jobs.empty?

  content_with_placeholders = wrap_headings_with_placeholders(content_with_placeholders)

  $stderr.puts "[sakusei] rendering #{jobs.length} Vue component(s)..."
  results = render_batch(jobs)
  $stderr.puts "[sakusei] Vue components rendered"
  result_map = results.each_with_object({}) { |r, h| h[r['id']] = r }

  all_css = []
  output = content_with_placeholders.gsub(/<!-- sakusei-vue-(\d+) -->/) do
    id = Regexp.last_match(1).to_i
    result = result_map[id]
    all_css << result['css'] if result&.fetch('css', '')&.length&.positive?
    result ? result['html'] : '<!-- Vue component render error -->'
  end

  if all_css.any?
    "<style>\n#{all_css.join("\n\n")}\n</style>\n\n#{output}"
  else
    output
  end
end