Class: Ryac::Minifier
- Inherits:
-
Object
- Object
- Ryac::Minifier
- Includes:
- Pipeline::SourcePatcher
- Defined in:
- lib/ryac/minifier.rb,
sig/ryac/minifier.rbs
Constant Summary collapse
- OPTIMIZE_PRE =
Stage lists are ordered — phase is list position. The optimizers that run before the rename batch:
[ # ControlFlowSimplify must precede EndlessMethod: a def body has to # collapse to a single statement before the def can become endless. [Pipeline::ControlFlowSimplify], [Pipeline::EndlessMethod], [Pipeline::ConstantFold], [Pipeline::BooleanShorten], [Pipeline::CharShorten], [Pipeline::SpellingShorten], ].freeze
- OPTIMIZE_POST =
Paren removal reasons about the final spelling of every call, so it runs after all renames.
[ [Pipeline::ParenOptimizer], ].freeze
- DEFAULT_LEVEL =
:stable- ALL_VAR_WITH_ATTR =
{ features: { keywords: true, ivars: true, cvars: true, gvars: true, attr_ivars: true } }.freeze
- STABLE_STAGES =
The two levels, named for their promise rather than a number.
:stable is the boundary the optcarrot test certifies frame-for-frame on a real program: class, constant and variable renaming, plus method renaming under the :safe policy — a group renames only when type inference resolved every caller and no dynamic escape hatch (a string mention, a dynamic-ivar class, an uncalled def) touches its name. "attrs renamed" is one fact spelled by three co-moving switches: AttrDeclShorten rename_attrs (declarations), attr_ivars (backing ivars) and MethodRenamer (call sites) — all three live here together.
:unstable switches method renaming to :aggressive, which a program can defeat by construction — names survive inside strings, eval'd source and computed send targets, out of reach of any static analysis. It is certified by self-hosting and works only when the program plays along.
Finer configurations are not levels: individual steps stay composable by passing an explicit stage list in place of a level name.
[ *OPTIMIZE_PRE, [Pipeline::ConstantAliaser, { rename_classes: true }], [Pipeline::AttrDeclShorten, { rename_attrs: true }], [Pipeline::VariableRenamer, ALL_VAR_WITH_ATTR], [Pipeline::MethodRenamer, { policy: :safe }], *OPTIMIZE_POST, ].freeze
- STAGES =
{ stable: STABLE_STAGES, unstable: derive_unstable(STABLE_STAGES), }.freeze
Instance Attribute Summary collapse
-
#result ⇒ Pipeline::MinifiedResult?
readonly
Returns the value of attribute result.
Class Method Summary collapse
-
.derive_unstable(stable) ⇒ Array[stage_entry]
:unstable is derived from :stable, never hand-copied — the superset law lives here as code.
- .resolve_level(value) ⇒ Symbol
-
.run_stages(code, stages, stdlib_requires: [], rbs_files: {}, lazy_files: []) ⇒ Pipeline::RenameResult
code is raw (uncompacted) text — compaction is the runner's fixed first step, so every stage list starts from the same dialect.
Instance Method Summary collapse
- #build_output(code, stdlib_requires, preamble = '') ⇒ String
- #build_result(rename_result, source, file_count) ⇒ Pipeline::MinifiedResult
- #call(entry_path, level: DEFAULT_LEVEL, project_root: nil, gem_names: [], gem_require_paths: []) ⇒ Pipeline::MinifiedResult
-
#initialize ⇒ Minifier
constructor
A new instance of Minifier.
-
#run_pipeline(source, target_level) ⇒ Pipeline::RenameResult
target_level is a preset name, or — for callers composing their own pipeline out of steps — an explicit stage list.
Methods included from Pipeline::SourcePatcher
#apply_patches, #mk, #src, #verify_parses
Constructor Details
#initialize ⇒ Minifier
Returns a new instance of Minifier.
112 113 114 115 |
# File 'lib/ryac/minifier.rb', line 112 def initialize @file_collector = Pipeline::FileCollector.new @concatenator = Pipeline::Concatenator.new end |
Instance Attribute Details
#result ⇒ Pipeline::MinifiedResult? (readonly)
Returns the value of attribute result.
110 111 112 |
# File 'lib/ryac/minifier.rb', line 110 def result @result end |
Class Method Details
.derive_unstable(stable) ⇒ Array[stage_entry]
:unstable is derived from :stable, never hand-copied — the superset law lives here as code. The one switch between them is the method renamer's policy: :safe touches only names whose every caller type inference resolved; :aggressive also takes the bets.
61 62 63 64 65 66 |
# File 'lib/ryac/minifier.rb', line 61 def self.derive_unstable(stable) stable.map { |entry| entry[0].equal?(Pipeline::MethodRenamer) ? [Pipeline::MethodRenamer] #: stage_entry : entry }.freeze end |
.resolve_level(value) ⇒ Symbol
48 49 50 51 52 53 |
# File 'lib/ryac/minifier.rb', line 48 def self.resolve_level(value) level = value.to_sym return level if STAGES.key?(level) raise ArgumentError, "Invalid compress level: #{value} (valid: #{STAGES.keys.join(', ')})" end |
.run_stages(code, stages, stdlib_requires: [], rbs_files: {}, lazy_files: []) ⇒ Pipeline::RenameResult
code is raw (uncompacted) text — compaction is the runner's fixed first step, so every stage list starts from the same dialect.
103 104 105 106 |
# File 'lib/ryac/minifier.rb', line 103 def self.run_stages(code, stages, stdlib_requires: [], rbs_files: {}, lazy_files: []) Pipeline::StageRunner.new(stdlib_requires: stdlib_requires, rbs_files: rbs_files, lazy_files: lazy_files) .call(code, stages) end |
Instance Method Details
#build_output(code, stdlib_requires, preamble = '') ⇒ String
163 164 165 166 167 168 |
# File 'lib/ryac/minifier.rb', line 163 def build_output(code, stdlib_requires, preamble = '') parts = stdlib_requires.map { |lib| "require \"#{lib}\"" } parts << preamble unless preamble.empty? parts << code parts.join(';') end |
#build_result(rename_result, source, file_count) ⇒ Pipeline::MinifiedResult
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/ryac/minifier.rb', line 141 def build_result(rename_result, source, file_count) content = build_output(rename_result.code, source.stdlib_requires, rename_result.preamble) # code and aliases were verified at the runner's exit; the assembled # content is the one string the runner never sees. verify_parses(content, 'content') size = content.bytesize stats = Pipeline::CompressionStats.new( original_size: source.original_size, minified_size: size, compression_ratio: size.to_f / source.original_size, file_count: file_count ) @result = Pipeline::MinifiedResult.new( content: content, aliases: rename_result.aliases, preamble: rename_result.preamble, stats: stats ) end |
#call(entry_path, level: DEFAULT_LEVEL, project_root: nil, gem_names: [], gem_require_paths: []) ⇒ Pipeline::MinifiedResult
117 118 119 120 121 122 123 124 125 126 |
# File 'lib/ryac/minifier.rb', line 117 def call(entry_path, level: DEFAULT_LEVEL, project_root: nil, gem_names: [], gem_require_paths: []) graph = @file_collector.call(entry_path, project_root: project_root, gem_names: gem_names, gem_require_paths: gem_require_paths) source = @concatenator.call(graph) # Captured while the boundaries still describe the text; from # compaction on they are input provenance, not current positions. file_count = source.file_boundaries.size result = run_pipeline(source, level) build_result(result, source, file_count) end |
#run_pipeline(source, target_level) ⇒ Pipeline::RenameResult
target_level is a preset name, or — for callers composing their own pipeline out of steps — an explicit stage list.
132 133 134 135 136 137 138 139 |
# File 'lib/ryac/minifier.rb', line 132 def run_pipeline(source, target_level) stages = target_level.is_a?(Array) ? target_level : STAGES.fetch(self.class.resolve_level(target_level)) self.class.run_stages(source.content, stages, stdlib_requires: source.stdlib_requires, rbs_files: source.rbs_files, lazy_files: source.lazy_files ) end |