Class: Lilac::CLI::PageCompiler

Inherits:
Object
  • Object
show all
Defined in:
lib/lilac/cli/build/page_compiler.rb

Overview

Compiles one source pages/*.html file into a dist HTML, given the build-level BuildContext. Handles:

- page-inline `<script type="text/ruby">` extraction
- page-inline `<X data-component=...>` synthesis (Nokogiri pass)
- data-use=X reference scanning
- per-target / per-delivery dispatch (4 combos: full×inline,
full×bundle, compiled×inline, compiled×bundle — including the
chained .mrb boot module for compiled×bundle×page-inline)

The outer Builder.build constructs the context once and invokes compile(page_path) for each page; the compiler itself holds no build-level state. See ADR-29 for the data-component / data-use split and decisions §20.6 for the Lilac.start placement rationale.

Defined Under Namespace

Classes: Injection

Constant Summary collapse

DATA_USE_PATTERN =

Matches data-use="X" / data-use='X' attribute values on any element. We don't need to know which tag carries the attribute — just collect the referenced component names so the build can bundle their templates + scripts into the page.

%r{
  \bdata-use\s*=\s*(?:"([^"]+)"|'([^']+)')
}x

Instance Method Summary collapse

Constructor Details

#initialize(context) ⇒ PageCompiler

Returns a new instance of PageCompiler.



48
49
50
51
52
53
# File 'lib/lilac/cli/build/page_compiler.rb', line 48

def initialize(context)
  @ctx = context
  @scripts_assembler = ComponentScriptsAssembler.new(
    template_cache: context.template_cache
  )
end

Instance Method Details

#compile(page_path) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/lilac/cli/build/page_compiler.rb', line 55

def compile(page_path)
  html = File.read(page_path)

  # Inline `<script type="text/ruby">` blocks in the page itself are
  # surfaced to the injection step so they're not silently dropped on
  # the compiled target. The tags themselves stay in the dist HTML
  # for both targets:
  #   - target=full — the runtime parser evaluates them in place
  #     via `vm.evalScript`
  #   - target=compiled — the wasm has no parser so the tags are
  #     dead text (browser ignores `text/ruby`), but they remain
  #     so "view source" works, and features like the 7guis
  #     `source-display` mirror still find the page's Ruby. Size
  #     cost is marginal (a few KB, fully compressible) and the
  #     `.mrb` value prop (no parser in wasm) is unaffected
  extracted = SFC.extract_inline_ruby_scripts(html, path: page_path)
  page_inline_scripts = extracted[:scripts]

  # Page-inline `<X data-component="...">` elements are folded into
  # the same pipeline that handles `.lil` components: we register
  # an in-memory `SFC::Component` for each (snapshot of the
  # element's outer HTML). data-each rows stay IN-PLACE (the runtime
  # scanner snapshots the live innerHTML and clones per item), and
  # runtime resolves `refs.lilN` positionally, so nothing else
  # about the page markup needs to change.
  components, html, used_inline, synthesized_names = synthesize_page_inline_components(
    html, page_path: page_path
  )

  # R4: page-inline script classes that collide with `.lil`-derived
  # class names (project-global) are flagged here. Done AFTER
  # synthesize_page_inline_components so synthesized in-memory
  # components (which carry no script) don't trigger false positives,
  # but BEFORE build_injection so the user sees a structured error
  # instead of a downstream mrbc failure.
  @ctx.build_linter.check_class_name_collisions!(page_inline_scripts, components, synthesized_names, page_path)

  used = used_inline.dup
  # Page references to components via data-use="X" — the runtime
  # injects markup from the matching <template>...<div data-component="X">
  # definition that build_injection emits below. We only need to
  # record each X in `used` so its template + script land in the
  # injection bundle.
  html.scan(DATA_USE_PATTERN) do |dq, sq|
    name = dq || sq
    unless components.key?(name)
      raise Builder::Error,
            "Unknown component referenced by data-use=#{name.inspect} in #{page_path} " \
            "(no components/#{name}.lil and no page-inline data-component=#{name.inspect})"
    end
    used << name
  end

  html =
    if @ctx.delivery == :bundle
      inject_bundle_page(html, page_path, used, components,
                         page_inline_scripts: page_inline_scripts,
                         synthesized_names: synthesized_names)
    else
      inject_inline_page(html, page_path, used, components,
                         page_inline_scripts: page_inline_scripts,
                         synthesized_names: synthesized_names)
    end

  File.write(output_path_for(page_path).tap { |p| FileUtils.mkdir_p(File.dirname(p)) }, html)
end