1use serde::Serialize;
4
5use super::{
6 TransformDagEdgeV0, TransformPassKind, all_transform_pass_kinds, default_transform_dag_edges,
7 transform_pass_execution_phase,
8};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)]
11#[serde(rename_all = "camelCase")]
12pub enum ObservationKindV0 {
13 SelectorMatching,
14 CascadeWinner,
15 CascadeWinnerEquality,
16 ExportedClassNames,
17 CustomPropertyComputedValue,
18 KeyframesReachability,
19 SourceMapTrace,
20 LayerRank,
21 Specificity,
22 Inheritance,
23 DeclarationOrder,
24 TargetPredicate,
25 ModuleResolution,
26 ImportContext,
27 ValueGraphReachability,
28 SemanticMarker,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)]
32#[serde(rename_all = "camelCase")]
33pub enum PassAssumptionKindV0 {
34 TokenBoundary,
35 SourceMapProvenance,
36 EquivalentLiteralValue,
37 SelectorSpecificity,
38 LonghandShorthandEquivalence,
39 DeclarationOrder,
40 TargetEnvironment,
41 Directionality,
42 NestedSelectorExpansion,
43 ScopedMatching,
44 LayerOrder,
45 StaticPredicate,
46 ImportWrapperProvenance,
47 ModuleNamespace,
48 SelectorIdentityMap,
49 ValueGraph,
50 CustomPropertyFixedPoint,
51 ClosedWorldReachability,
52 EmissionTrace,
53 SemanticMarkerRetention,
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
57#[serde(rename_all = "camelCase")]
58pub struct PassSemanticContractV0 {
59 pub observes: Vec<ObservationKindV0>,
60 pub preserves: Vec<ObservationKindV0>,
61 pub requires: Vec<PassAssumptionKindV0>,
62}
63
64#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
65#[serde(rename_all = "camelCase")]
66pub enum PassObservationSurfaceV0 {
67 Declared(PassSemanticContractV0),
68 UnknownGap { reason: &'static str },
69}
70
71impl PassObservationSurfaceV0 {
72 pub fn is_declared(&self) -> bool {
73 matches!(self, Self::Declared(_))
74 }
75
76 pub fn gap_reason(&self) -> Option<&'static str> {
77 match self {
78 Self::Declared(_) => None,
79 Self::UnknownGap { reason } => Some(reason),
80 }
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
85#[serde(rename_all = "camelCase")]
86pub struct TransformPassObservationRecordV0 {
87 pub schema_version: &'static str,
88 pub product: &'static str,
89 pub id: &'static str,
90 pub kind: TransformPassKind,
91 pub surface: PassObservationSurfaceV0,
92}
93
94#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)]
95#[serde(rename_all = "camelCase")]
96pub enum TransformPassClassV0 {
97 Structural,
98 TextLocal,
99 ModuleEvaluation,
100 Emission,
101}
102
103#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
104#[serde(rename_all = "camelCase")]
105pub struct TransformPassDescriptorV0 {
106 pub schema_version: &'static str,
107 pub product: &'static str,
108 pub id: &'static str,
109 pub kind: TransformPassKind,
110 pub pass_class: TransformPassClassV0,
111 pub phase: u8,
112 pub phase_order: u16,
113 pub depends_on: Vec<&'static str>,
114 pub conflicts_with: Vec<&'static str>,
115}
116
117impl TransformPassDescriptorV0 {
118 pub fn is_structural(&self) -> bool {
119 self.pass_class == TransformPassClassV0::Structural
120 }
121
122 pub fn keeps_text_local_slice_rewrite(&self) -> bool {
123 self.pass_class == TransformPassClassV0::TextLocal
124 }
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
128#[serde(rename_all = "camelCase")]
129pub struct TransformBuildProfileV0 {
130 pub schema_version: &'static str,
131 pub product: &'static str,
132 pub profile_id: &'static str,
133 pub pass_ids: Vec<&'static str>,
134}
135
136pub const STRICT_VERIFICATION_BUILD_PROFILE_ID_V0: &str = "strict-verification";
137
138#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
139#[serde(rename_all = "camelCase")]
140pub struct TransformStrictPolicyDescriptorV0 {
141 pub schema_version: &'static str,
142 pub product: &'static str,
143 pub profile_id: &'static str,
144 pub refuse_unknown_pass_ids: bool,
145 pub require_closed_world_evidence: bool,
146 pub require_complete_decisions: bool,
147 pub enforce_winner_equality: bool,
148}
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)]
151#[serde(rename_all = "camelCase")]
152pub enum MinifyPassProfileClassV0 {
153 Safe,
154 Semantic,
155 ClosedWorldOnly,
156 Excluded,
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
160#[serde(rename_all = "camelCase")]
161pub enum MinifyPassClassificationDerivationV0 {
162 ClosedWorldRequirement,
163 Policy,
164}
165
166#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
167#[serde(rename_all = "camelCase")]
168pub struct MinifyPassClassificationV0 {
169 pub pass_id: &'static str,
170 pub kind: TransformPassKind,
171 pub profile_class: MinifyPassProfileClassV0,
172 pub derivation: MinifyPassClassificationDerivationV0,
173 pub reason: &'static str,
174}
175
176pub const fn transform_pass_requires_closed_world_bundle(kind: TransformPassKind) -> bool {
177 matches!(
178 kind,
179 TransformPassKind::LayerFlatten
180 | TransformPassKind::TreeShakeClass
181 | TransformPassKind::TreeShakeKeyframes
182 | TransformPassKind::TreeShakeValue
183 | TransformPassKind::TreeShakeCustomProperty
184 )
185}
186
187pub const fn minify_pass_profile_classification(
188 kind: TransformPassKind,
189) -> MinifyPassClassificationV0 {
190 let (profile_class, derivation, reason) = match kind {
191 TransformPassKind::WhitespaceStrip
192 | TransformPassKind::CommentStrip
193 | TransformPassKind::NumberCompression
194 | TransformPassKind::UnitNormalization
195 | TransformPassKind::ColorCompression
196 | TransformPassKind::UrlQuoteStrip
197 | TransformPassKind::StringQuoteNormalize
198 | TransformPassKind::CalcReduction
199 | TransformPassKind::PrintCss => (
200 MinifyPassProfileClassV0::Safe,
201 MinifyPassClassificationDerivationV0::Policy,
202 "local representation rewrite with no workspace reachability requirement",
203 ),
204 TransformPassKind::SelectorIsWhereCompression
205 | TransformPassKind::ShorthandCombining
206 | TransformPassKind::RuleDeduplication
207 | TransformPassKind::RuleMerging
208 | TransformPassKind::SelectorMerging
209 | TransformPassKind::EmptyRuleRemoval
210 | TransformPassKind::StalePrefixRemoval
211 | TransformPassKind::ValueResolution
212 | TransformPassKind::StaticVarSubstitution => (
213 MinifyPassProfileClassV0::Semantic,
214 MinifyPassClassificationDerivationV0::Policy,
215 "requires cascade, value, or semantic evidence beyond a local representation rewrite",
216 ),
217 TransformPassKind::LayerFlatten
218 | TransformPassKind::TreeShakeClass
219 | TransformPassKind::TreeShakeKeyframes
220 | TransformPassKind::TreeShakeValue
221 | TransformPassKind::TreeShakeCustomProperty => (
222 MinifyPassProfileClassV0::ClosedWorldOnly,
223 MinifyPassClassificationDerivationV0::ClosedWorldRequirement,
224 "requires a closed-world bundle before mutation",
225 ),
226 TransformPassKind::VendorPrefixing
227 | TransformPassKind::LightDarkLowering
228 | TransformPassKind::ColorMixLowering
229 | TransformPassKind::OklchOklabLowering
230 | TransformPassKind::ColorFunctionLowering
231 | TransformPassKind::RelativeColorLowering
232 | TransformPassKind::LogicalToPhysical
233 | TransformPassKind::NestingUnwrap
234 | TransformPassKind::ScopeFlatten
235 | TransformPassKind::SupportsStaticEval
236 | TransformPassKind::MediaStaticEval
237 | TransformPassKind::ContainerStaticEval
238 | TransformPassKind::NativeCssStaticEval
239 | TransformPassKind::ImportInline
240 | TransformPassKind::ScssModuleEvaluate
241 | TransformPassKind::LessModuleEvaluate
242 | TransformPassKind::HashCssModuleClassNames
243 | TransformPassKind::ResolveCssModulesComposes
244 | TransformPassKind::DeadMediaBranchRemoval
245 | TransformPassKind::DeadSupportsBranchRemoval
246 | TransformPassKind::DesignTokenRouting => (
247 MinifyPassProfileClassV0::Excluded,
248 MinifyPassClassificationDerivationV0::Policy,
249 "belongs to target lowering, module evaluation, identity rewriting, or another product surface",
250 ),
251 };
252
253 MinifyPassClassificationV0 {
254 pass_id: kind.id(),
255 kind,
256 profile_class,
257 derivation,
258 reason,
259 }
260}
261
262pub fn default_minify_pass_classifications() -> Vec<MinifyPassClassificationV0> {
263 all_transform_pass_kinds()
264 .into_iter()
265 .map(minify_pass_profile_classification)
266 .collect()
267}
268
269pub fn safe_minify_build_profile() -> TransformBuildProfileV0 {
270 minify_build_profile("safe", MinifyPassProfileClassV0::Safe)
271}
272
273pub fn semantic_minify_build_profile() -> TransformBuildProfileV0 {
274 minify_build_profile("semantic", MinifyPassProfileClassV0::Semantic)
275}
276
277pub fn closed_world_minify_build_profile() -> TransformBuildProfileV0 {
278 minify_build_profile("closed-world", MinifyPassProfileClassV0::ClosedWorldOnly)
279}
280
281pub fn default_minify_build_profiles() -> [TransformBuildProfileV0; 3] {
282 [
283 safe_minify_build_profile(),
284 semantic_minify_build_profile(),
285 closed_world_minify_build_profile(),
286 ]
287}
288
289pub fn strict_verification_build_profile() -> TransformBuildProfileV0 {
290 let profile_classes = [
291 MinifyPassProfileClassV0::Semantic,
292 MinifyPassProfileClassV0::ClosedWorldOnly,
293 MinifyPassProfileClassV0::Excluded,
294 ];
295 let mut passes = profile_classes
296 .into_iter()
297 .flat_map(|profile_class| {
298 default_minify_pass_classifications()
299 .into_iter()
300 .filter(move |entry| entry.profile_class == profile_class)
301 })
302 .filter(|entry| pass_requires_strict_winner_verification(entry.kind))
303 .map(|entry| entry.kind)
304 .collect::<Vec<_>>();
305 passes.sort_by_key(|pass| pass.ordinal());
306
307 transform_build_profile_from_passes(STRICT_VERIFICATION_BUILD_PROFILE_ID_V0, &passes)
308}
309
310pub fn strict_verification_policy_descriptor() -> TransformStrictPolicyDescriptorV0 {
311 TransformStrictPolicyDescriptorV0 {
312 schema_version: "0",
313 product: "omena-transform-cst.strict-policy",
314 profile_id: STRICT_VERIFICATION_BUILD_PROFILE_ID_V0,
315 refuse_unknown_pass_ids: true,
316 require_closed_world_evidence: true,
317 require_complete_decisions: true,
318 enforce_winner_equality: true,
319 }
320}
321
322pub fn strict_policy_descriptor_for_profile(
323 profile_id: &str,
324) -> Option<TransformStrictPolicyDescriptorV0> {
325 (profile_id == STRICT_VERIFICATION_BUILD_PROFILE_ID_V0)
326 .then(strict_verification_policy_descriptor)
327}
328
329fn pass_requires_strict_winner_verification(kind: TransformPassKind) -> bool {
330 let PassObservationSurfaceV0::Declared(contract) = pass_observation_contract(kind) else {
331 return false;
332 };
333 contract
334 .preserves
335 .contains(&ObservationKindV0::CascadeWinnerEquality)
336 && !contract
337 .preserves
338 .contains(&ObservationKindV0::SemanticMarker)
339 && !contract
340 .requires
341 .contains(&PassAssumptionKindV0::ClosedWorldReachability)
342}
343
344fn minify_build_profile(
345 profile_id: &'static str,
346 maximum_class: MinifyPassProfileClassV0,
347) -> TransformBuildProfileV0 {
348 let pass_ids = default_minify_pass_classifications()
349 .into_iter()
350 .filter(|entry| {
351 entry.profile_class != MinifyPassProfileClassV0::Excluded
352 && entry.profile_class <= maximum_class
353 })
354 .map(|entry| entry.pass_id)
355 .collect();
356 TransformBuildProfileV0 {
357 schema_version: "0",
358 product: "omena-transform-cst.build-profile",
359 profile_id,
360 pass_ids,
361 }
362}
363
364pub fn transform_build_profile_from_passes(
365 profile_id: &'static str,
366 passes: &[TransformPassKind],
367) -> TransformBuildProfileV0 {
368 TransformBuildProfileV0 {
369 schema_version: "0",
370 product: "omena-transform-cst.build-profile",
371 profile_id,
372 pass_ids: passes.iter().map(|pass| pass.id()).collect(),
373 }
374}
375
376pub fn default_transform_pass_descriptors() -> Vec<TransformPassDescriptorV0> {
377 let dag_edges = default_transform_dag_edges();
378 all_transform_pass_kinds()
379 .into_iter()
380 .map(|kind| transform_pass_descriptor_with_edges(kind, dag_edges.as_slice()))
381 .collect()
382}
383
384pub fn transform_pass_descriptor(kind: TransformPassKind) -> TransformPassDescriptorV0 {
385 let dag_edges = default_transform_dag_edges();
386 transform_pass_descriptor_with_edges(kind, dag_edges.as_slice())
387}
388
389pub fn default_transform_pass_observation_records() -> Vec<TransformPassObservationRecordV0> {
390 all_transform_pass_kinds()
391 .into_iter()
392 .map(|kind| TransformPassObservationRecordV0 {
393 schema_version: "0",
394 product: "omena-transform-cst.pass-observation-surface",
395 id: kind.id(),
396 kind,
397 surface: pass_observation_contract(kind),
398 })
399 .collect()
400}
401
402pub fn pass_observation_contract(kind: TransformPassKind) -> PassObservationSurfaceV0 {
403 match kind {
404 TransformPassKind::WhitespaceStrip => declared_observation_contract(
405 &[ObservationKindV0::SourceMapTrace],
406 &[
407 ObservationKindV0::SelectorMatching,
408 ObservationKindV0::CascadeWinner,
409 ObservationKindV0::DeclarationOrder,
410 ObservationKindV0::SourceMapTrace,
411 ],
412 &[PassAssumptionKindV0::TokenBoundary],
413 ),
414 TransformPassKind::CommentStrip => declared_observation_contract(
415 &[ObservationKindV0::SourceMapTrace],
416 &[
417 ObservationKindV0::SelectorMatching,
418 ObservationKindV0::CascadeWinner,
419 ObservationKindV0::DeclarationOrder,
420 ObservationKindV0::SourceMapTrace,
421 ],
422 &[PassAssumptionKindV0::SourceMapProvenance],
423 ),
424 TransformPassKind::NumberCompression
425 | TransformPassKind::UnitNormalization
426 | TransformPassKind::ColorCompression
427 | TransformPassKind::UrlQuoteStrip
428 | TransformPassKind::StringQuoteNormalize => declared_observation_contract(
429 &[
430 ObservationKindV0::CascadeWinner,
431 ObservationKindV0::CustomPropertyComputedValue,
432 ObservationKindV0::SourceMapTrace,
433 ],
434 &[
435 ObservationKindV0::CascadeWinner,
436 ObservationKindV0::CustomPropertyComputedValue,
437 ObservationKindV0::SourceMapTrace,
438 ],
439 &[PassAssumptionKindV0::EquivalentLiteralValue],
440 ),
441 TransformPassKind::SelectorIsWhereCompression => declared_observation_contract(
442 &[
443 ObservationKindV0::SelectorMatching,
444 ObservationKindV0::Specificity,
445 ObservationKindV0::KeyframesReachability,
446 ],
447 &[
448 ObservationKindV0::SelectorMatching,
449 ObservationKindV0::Specificity,
450 ObservationKindV0::KeyframesReachability,
451 ],
452 &[PassAssumptionKindV0::SelectorSpecificity],
453 ),
454 TransformPassKind::ShorthandCombining => declared_observation_contract(
455 &[
456 ObservationKindV0::CascadeWinner,
457 ObservationKindV0::DeclarationOrder,
458 ObservationKindV0::Inheritance,
459 ],
460 &[
461 ObservationKindV0::CascadeWinner,
462 ObservationKindV0::DeclarationOrder,
463 ObservationKindV0::Inheritance,
464 ],
465 &[PassAssumptionKindV0::LonghandShorthandEquivalence],
466 ),
467 TransformPassKind::RuleDeduplication | TransformPassKind::RuleMerging => {
468 declared_observation_contract(
469 &[
470 ObservationKindV0::CascadeWinner,
471 ObservationKindV0::CascadeWinnerEquality,
472 ObservationKindV0::LayerRank,
473 ObservationKindV0::Specificity,
474 ObservationKindV0::DeclarationOrder,
475 ],
476 &[
477 ObservationKindV0::CascadeWinner,
478 ObservationKindV0::CascadeWinnerEquality,
479 ObservationKindV0::LayerRank,
480 ObservationKindV0::Specificity,
481 ObservationKindV0::DeclarationOrder,
482 ],
483 &[PassAssumptionKindV0::DeclarationOrder],
484 )
485 }
486 TransformPassKind::SelectorMerging => declared_observation_contract(
487 &[
488 ObservationKindV0::CascadeWinnerEquality,
489 ObservationKindV0::SelectorMatching,
490 ObservationKindV0::Specificity,
491 ObservationKindV0::ExportedClassNames,
492 ],
493 &[
494 ObservationKindV0::CascadeWinnerEquality,
495 ObservationKindV0::SelectorMatching,
496 ObservationKindV0::Specificity,
497 ObservationKindV0::ExportedClassNames,
498 ],
499 &[
500 PassAssumptionKindV0::SelectorSpecificity,
501 PassAssumptionKindV0::SelectorIdentityMap,
502 ],
503 ),
504 TransformPassKind::EmptyRuleRemoval => declared_observation_contract(
505 &[
506 ObservationKindV0::CascadeWinnerEquality,
507 ObservationKindV0::SemanticMarker,
508 ObservationKindV0::SourceMapTrace,
509 ],
510 &[
511 ObservationKindV0::CascadeWinnerEquality,
512 ObservationKindV0::SemanticMarker,
513 ObservationKindV0::SourceMapTrace,
514 ],
515 &[PassAssumptionKindV0::SemanticMarkerRetention],
516 ),
517 TransformPassKind::VendorPrefixing
518 | TransformPassKind::StalePrefixRemoval
519 | TransformPassKind::LightDarkLowering
520 | TransformPassKind::ColorMixLowering
521 | TransformPassKind::OklchOklabLowering
522 | TransformPassKind::ColorFunctionLowering
523 | TransformPassKind::RelativeColorLowering => declared_observation_contract(
524 &[
525 ObservationKindV0::TargetPredicate,
526 ObservationKindV0::CascadeWinner,
527 ObservationKindV0::CustomPropertyComputedValue,
528 ],
529 &[
530 ObservationKindV0::CascadeWinner,
531 ObservationKindV0::CustomPropertyComputedValue,
532 ],
533 &[PassAssumptionKindV0::TargetEnvironment],
534 ),
535 TransformPassKind::LogicalToPhysical => declared_observation_contract(
536 &[
537 ObservationKindV0::CascadeWinner,
538 ObservationKindV0::Inheritance,
539 ],
540 &[
541 ObservationKindV0::CascadeWinner,
542 ObservationKindV0::Inheritance,
543 ],
544 &[PassAssumptionKindV0::Directionality],
545 ),
546 TransformPassKind::NestingUnwrap => declared_observation_contract(
547 &[
548 ObservationKindV0::CascadeWinnerEquality,
549 ObservationKindV0::SelectorMatching,
550 ObservationKindV0::Specificity,
551 ],
552 &[
553 ObservationKindV0::CascadeWinnerEquality,
554 ObservationKindV0::SelectorMatching,
555 ObservationKindV0::Specificity,
556 ],
557 &[PassAssumptionKindV0::NestedSelectorExpansion],
558 ),
559 TransformPassKind::ScopeFlatten => declared_observation_contract(
560 &[
561 ObservationKindV0::CascadeWinnerEquality,
562 ObservationKindV0::SelectorMatching,
563 ],
564 &[
565 ObservationKindV0::CascadeWinnerEquality,
566 ObservationKindV0::SelectorMatching,
567 ],
568 &[PassAssumptionKindV0::ScopedMatching],
569 ),
570 TransformPassKind::LayerFlatten => declared_observation_contract(
571 &[
572 ObservationKindV0::LayerRank,
573 ObservationKindV0::CascadeWinner,
574 ObservationKindV0::CascadeWinnerEquality,
575 ],
576 &[
577 ObservationKindV0::LayerRank,
578 ObservationKindV0::CascadeWinner,
579 ObservationKindV0::CascadeWinnerEquality,
580 ],
581 &[PassAssumptionKindV0::LayerOrder],
582 ),
583 TransformPassKind::SupportsStaticEval
584 | TransformPassKind::MediaStaticEval
585 | TransformPassKind::ContainerStaticEval
586 | TransformPassKind::NativeCssStaticEval => declared_observation_contract(
587 &[
588 ObservationKindV0::TargetPredicate,
589 ObservationKindV0::CascadeWinner,
590 ],
591 &[ObservationKindV0::CascadeWinner],
592 &[PassAssumptionKindV0::StaticPredicate],
593 ),
594 TransformPassKind::CalcReduction => declared_observation_contract(
595 &[
596 ObservationKindV0::CustomPropertyComputedValue,
597 ObservationKindV0::CascadeWinner,
598 ],
599 &[
600 ObservationKindV0::CustomPropertyComputedValue,
601 ObservationKindV0::CascadeWinner,
602 ],
603 &[PassAssumptionKindV0::EquivalentLiteralValue],
604 ),
605 TransformPassKind::ImportInline => declared_observation_contract(
606 &[
607 ObservationKindV0::ImportContext,
608 ObservationKindV0::LayerRank,
609 ObservationKindV0::CascadeWinner,
610 ObservationKindV0::SourceMapTrace,
611 ],
612 &[
613 ObservationKindV0::ImportContext,
614 ObservationKindV0::LayerRank,
615 ObservationKindV0::CascadeWinner,
616 ObservationKindV0::SourceMapTrace,
617 ],
618 &[PassAssumptionKindV0::ImportWrapperProvenance],
619 ),
620 TransformPassKind::ScssModuleEvaluate | TransformPassKind::LessModuleEvaluate => {
621 declared_observation_contract(
622 &[
623 ObservationKindV0::ModuleResolution,
624 ObservationKindV0::CustomPropertyComputedValue,
625 ObservationKindV0::SourceMapTrace,
626 ],
627 &[
628 ObservationKindV0::ModuleResolution,
629 ObservationKindV0::CustomPropertyComputedValue,
630 ObservationKindV0::SourceMapTrace,
631 ],
632 &[PassAssumptionKindV0::ModuleNamespace],
633 )
634 }
635 TransformPassKind::HashCssModuleClassNames => declared_observation_contract(
636 &[
637 ObservationKindV0::ExportedClassNames,
638 ObservationKindV0::SelectorMatching,
639 ],
640 &[
641 ObservationKindV0::ExportedClassNames,
642 ObservationKindV0::SelectorMatching,
643 ],
644 &[PassAssumptionKindV0::SelectorIdentityMap],
645 ),
646 TransformPassKind::ResolveCssModulesComposes => declared_observation_contract(
647 &[
648 ObservationKindV0::ExportedClassNames,
649 ObservationKindV0::ModuleResolution,
650 ],
651 &[
652 ObservationKindV0::ExportedClassNames,
653 ObservationKindV0::ModuleResolution,
654 ],
655 &[PassAssumptionKindV0::SelectorIdentityMap],
656 ),
657 TransformPassKind::ValueResolution => declared_observation_contract(
658 &[ObservationKindV0::ValueGraphReachability],
659 &[ObservationKindV0::ValueGraphReachability],
660 &[PassAssumptionKindV0::ValueGraph],
661 ),
662 TransformPassKind::StaticVarSubstitution => declared_observation_contract(
663 &[ObservationKindV0::CustomPropertyComputedValue],
664 &[ObservationKindV0::CustomPropertyComputedValue],
665 &[PassAssumptionKindV0::CustomPropertyFixedPoint],
666 ),
667 TransformPassKind::TreeShakeClass => declared_observation_contract(
668 &[
669 ObservationKindV0::CascadeWinnerEquality,
670 ObservationKindV0::ExportedClassNames,
671 ObservationKindV0::SelectorMatching,
672 ],
673 &[
674 ObservationKindV0::CascadeWinnerEquality,
675 ObservationKindV0::ExportedClassNames,
676 ObservationKindV0::SelectorMatching,
677 ],
678 &[PassAssumptionKindV0::ClosedWorldReachability],
679 ),
680 TransformPassKind::TreeShakeKeyframes => declared_observation_contract(
681 &[
682 ObservationKindV0::CascadeWinnerEquality,
683 ObservationKindV0::KeyframesReachability,
684 ],
685 &[
686 ObservationKindV0::CascadeWinnerEquality,
687 ObservationKindV0::KeyframesReachability,
688 ],
689 &[PassAssumptionKindV0::ClosedWorldReachability],
690 ),
691 TransformPassKind::TreeShakeValue => declared_observation_contract(
692 &[
693 ObservationKindV0::CascadeWinnerEquality,
694 ObservationKindV0::ValueGraphReachability,
695 ],
696 &[
697 ObservationKindV0::CascadeWinnerEquality,
698 ObservationKindV0::ValueGraphReachability,
699 ],
700 &[PassAssumptionKindV0::ClosedWorldReachability],
701 ),
702 TransformPassKind::TreeShakeCustomProperty => declared_observation_contract(
703 &[
704 ObservationKindV0::CascadeWinnerEquality,
705 ObservationKindV0::CustomPropertyComputedValue,
706 ObservationKindV0::ValueGraphReachability,
707 ],
708 &[
709 ObservationKindV0::CascadeWinnerEquality,
710 ObservationKindV0::CustomPropertyComputedValue,
711 ObservationKindV0::ValueGraphReachability,
712 ],
713 &[PassAssumptionKindV0::ClosedWorldReachability],
714 ),
715 TransformPassKind::DeadMediaBranchRemoval
716 | TransformPassKind::DeadSupportsBranchRemoval => declared_observation_contract(
717 &[
718 ObservationKindV0::TargetPredicate,
719 ObservationKindV0::CascadeWinner,
720 ],
721 &[ObservationKindV0::CascadeWinner],
722 &[PassAssumptionKindV0::StaticPredicate],
723 ),
724 TransformPassKind::DesignTokenRouting => declared_observation_contract(
725 &[
726 ObservationKindV0::CustomPropertyComputedValue,
727 ObservationKindV0::ModuleResolution,
728 ],
729 &[
730 ObservationKindV0::CustomPropertyComputedValue,
731 ObservationKindV0::ModuleResolution,
732 ],
733 &[PassAssumptionKindV0::ModuleNamespace],
734 ),
735 TransformPassKind::PrintCss => declared_observation_contract(
736 &[ObservationKindV0::SourceMapTrace],
737 &[ObservationKindV0::SourceMapTrace],
738 &[PassAssumptionKindV0::EmissionTrace],
739 ),
740 }
741}
742
743fn declared_observation_contract(
744 observes: &[ObservationKindV0],
745 preserves: &[ObservationKindV0],
746 requires: &[PassAssumptionKindV0],
747) -> PassObservationSurfaceV0 {
748 PassObservationSurfaceV0::Declared(PassSemanticContractV0 {
749 observes: observes.to_vec(),
750 preserves: preserves.to_vec(),
751 requires: requires.to_vec(),
752 })
753}
754
755pub const fn transform_pass_class(kind: TransformPassKind) -> TransformPassClassV0 {
756 match kind {
757 TransformPassKind::NestingUnwrap
758 | TransformPassKind::ScopeFlatten
759 | TransformPassKind::LayerFlatten
760 | TransformPassKind::RuleDeduplication
761 | TransformPassKind::RuleMerging
762 | TransformPassKind::SelectorMerging
763 | TransformPassKind::EmptyRuleRemoval
764 | TransformPassKind::SupportsStaticEval
765 | TransformPassKind::MediaStaticEval
766 | TransformPassKind::ContainerStaticEval
767 | TransformPassKind::NativeCssStaticEval
768 | TransformPassKind::DeadMediaBranchRemoval
769 | TransformPassKind::DeadSupportsBranchRemoval
770 | TransformPassKind::TreeShakeClass
771 | TransformPassKind::TreeShakeKeyframes
772 | TransformPassKind::TreeShakeValue
773 | TransformPassKind::TreeShakeCustomProperty
774 | TransformPassKind::ResolveCssModulesComposes
775 | TransformPassKind::HashCssModuleClassNames
776 | TransformPassKind::ImportInline
777 | TransformPassKind::DesignTokenRouting => TransformPassClassV0::Structural,
778 TransformPassKind::ScssModuleEvaluate | TransformPassKind::LessModuleEvaluate => {
779 TransformPassClassV0::ModuleEvaluation
780 }
781 TransformPassKind::PrintCss => TransformPassClassV0::Emission,
782 _ => TransformPassClassV0::TextLocal,
783 }
784}
785
786const fn transform_pass_phase_order(kind: TransformPassKind) -> u16 {
787 match kind {
788 TransformPassKind::WhitespaceStrip => 10,
789 TransformPassKind::CommentStrip => 20,
790 TransformPassKind::NumberCompression => 30,
791 TransformPassKind::UnitNormalization => 40,
792 TransformPassKind::ColorCompression => 50,
793 TransformPassKind::UrlQuoteStrip => 60,
794 TransformPassKind::StringQuoteNormalize => 70,
795 TransformPassKind::SelectorIsWhereCompression => 80,
796 TransformPassKind::ShorthandCombining => 90,
797 TransformPassKind::RuleDeduplication => 100,
798 TransformPassKind::RuleMerging => 110,
799 TransformPassKind::SelectorMerging => 120,
800 TransformPassKind::EmptyRuleRemoval => 130,
801 TransformPassKind::VendorPrefixing => 140,
802 TransformPassKind::StalePrefixRemoval => 150,
803 TransformPassKind::LightDarkLowering => 160,
804 TransformPassKind::ColorMixLowering => 170,
805 TransformPassKind::OklchOklabLowering => 180,
806 TransformPassKind::ColorFunctionLowering => 190,
807 TransformPassKind::LogicalToPhysical => 200,
808 TransformPassKind::NestingUnwrap => 210,
809 TransformPassKind::ScopeFlatten => 220,
810 TransformPassKind::LayerFlatten => 230,
811 TransformPassKind::SupportsStaticEval => 240,
812 TransformPassKind::MediaStaticEval => 250,
813 TransformPassKind::CalcReduction => 260,
814 TransformPassKind::ImportInline => 270,
815 TransformPassKind::ScssModuleEvaluate => 280,
816 TransformPassKind::LessModuleEvaluate => 290,
817 TransformPassKind::HashCssModuleClassNames => 300,
818 TransformPassKind::ResolveCssModulesComposes => 310,
819 TransformPassKind::ValueResolution => 320,
820 TransformPassKind::StaticVarSubstitution => 330,
821 TransformPassKind::TreeShakeClass => 340,
822 TransformPassKind::TreeShakeKeyframes => 350,
823 TransformPassKind::TreeShakeValue => 360,
824 TransformPassKind::TreeShakeCustomProperty => 370,
825 TransformPassKind::DeadMediaBranchRemoval => 380,
826 TransformPassKind::DeadSupportsBranchRemoval => 390,
827 TransformPassKind::DesignTokenRouting => 400,
828 TransformPassKind::PrintCss => 410,
829 TransformPassKind::RelativeColorLowering => 420,
830 TransformPassKind::ContainerStaticEval => 430,
831 TransformPassKind::NativeCssStaticEval => 440,
832 }
833}
834
835fn transform_pass_descriptor_with_edges(
836 kind: TransformPassKind,
837 dag_edges: &[TransformDagEdgeV0],
838) -> TransformPassDescriptorV0 {
839 TransformPassDescriptorV0 {
840 schema_version: "0",
841 product: "omena-transform-cst.pass-descriptor",
842 id: kind.id(),
843 kind,
844 pass_class: transform_pass_class(kind),
845 phase: transform_pass_execution_phase(kind),
846 phase_order: transform_pass_phase_order(kind),
847 depends_on: dag_edges
848 .iter()
849 .filter(|edge| edge.to == kind.id())
850 .map(|edge| edge.from)
851 .collect(),
852 conflicts_with: transform_pass_conflicts_with(kind, dag_edges),
853 }
854}
855
856fn transform_pass_conflicts_with(
857 kind: TransformPassKind,
858 dag_edges: &[TransformDagEdgeV0],
859) -> Vec<&'static str> {
860 let Some(conflict_family) = transform_pass_conflict_family(kind) else {
861 return Vec::new();
862 };
863 all_transform_pass_kinds()
864 .into_iter()
865 .filter(|other| *other != kind)
866 .filter(|other| transform_pass_conflict_family(*other) == Some(conflict_family))
867 .filter(|other| {
868 transform_pass_execution_phase(*other) == transform_pass_execution_phase(kind)
869 })
870 .filter(|other| !dag_path_exists(kind.id(), other.id(), dag_edges))
871 .filter(|other| !dag_path_exists(other.id(), kind.id(), dag_edges))
872 .map(|other| other.id())
873 .collect()
874}
875
876fn transform_pass_conflict_family(kind: TransformPassKind) -> Option<&'static str> {
877 match kind {
878 TransformPassKind::ColorMixLowering | TransformPassKind::ColorFunctionLowering => {
879 Some("nested-color-function-lowering")
880 }
881 _ => None,
882 }
883}
884
885fn dag_path_exists(from: &'static str, to: &'static str, dag_edges: &[TransformDagEdgeV0]) -> bool {
886 let mut stack = vec![from];
887 let mut visited = Vec::new();
888 while let Some(current) = stack.pop() {
889 if current == to {
890 return true;
891 }
892 if visited.contains(¤t) {
893 continue;
894 }
895 visited.push(current);
896 for edge in dag_edges.iter().filter(|edge| edge.from == current) {
897 stack.push(edge.to);
898 }
899 }
900 false
901}
902
903#[cfg(test)]
904mod tests {
905 use super::*;
906 use std::collections::BTreeSet;
907
908 #[test]
909 fn minify_manifest_classifies_the_complete_pass_catalog() {
910 let manifest = default_minify_pass_classifications();
911 let pass_ids = manifest
912 .iter()
913 .map(|entry| entry.pass_id)
914 .collect::<BTreeSet<_>>();
915
916 assert_eq!(manifest.len(), crate::TRANSFORM_PASS_CATALOG_LEN);
917 assert_eq!(pass_ids.len(), crate::TRANSFORM_PASS_CATALOG_LEN);
918 assert!(manifest.iter().all(|entry| !entry.reason.is_empty()));
919 }
920
921 #[test]
922 fn minify_closed_world_classification_matches_the_runtime_gate() {
923 for entry in default_minify_pass_classifications() {
924 let requires_closed_world = transform_pass_requires_closed_world_bundle(entry.kind);
925 let classified_closed_world =
926 entry.profile_class == MinifyPassProfileClassV0::ClosedWorldOnly;
927 assert_eq!(
928 classified_closed_world, requires_closed_world,
929 "{}",
930 entry.pass_id
931 );
932 if classified_closed_world {
933 assert_eq!(
934 entry.derivation,
935 MinifyPassClassificationDerivationV0::ClosedWorldRequirement
936 );
937 } else {
938 assert_eq!(
939 entry.derivation,
940 MinifyPassClassificationDerivationV0::Policy
941 );
942 }
943 }
944 }
945
946 #[test]
947 fn minify_profiles_are_monotone_and_exclude_non_minify_passes() {
948 let [safe, semantic, closed_world] = default_minify_build_profiles();
949 let safe_passes = safe.pass_ids.iter().copied().collect::<BTreeSet<_>>();
950 let semantic_passes = semantic.pass_ids.iter().copied().collect::<BTreeSet<_>>();
951 let closed_world_passes = closed_world
952 .pass_ids
953 .iter()
954 .copied()
955 .collect::<BTreeSet<_>>();
956
957 assert_eq!(safe.profile_id, "safe");
958 assert_eq!(semantic.profile_id, "semantic");
959 assert_eq!(closed_world.profile_id, "closed-world");
960 assert!(safe_passes.is_subset(&semantic_passes));
961 assert!(semantic_passes.is_subset(&closed_world_passes));
962 assert!(safe_passes.len() < semantic_passes.len());
963 assert!(semantic_passes.len() < closed_world_passes.len());
964
965 for entry in default_minify_pass_classifications()
966 .into_iter()
967 .filter(|entry| entry.profile_class == MinifyPassProfileClassV0::Excluded)
968 {
969 assert!(!closed_world_passes.contains(entry.pass_id));
970 }
971 }
972
973 #[test]
974 fn strict_profile_is_derived_from_bucket_observation_contracts() {
975 let profile = strict_verification_build_profile();
976 let policy = strict_verification_policy_descriptor();
977 let expected = [
978 TransformPassKind::RuleDeduplication,
979 TransformPassKind::RuleMerging,
980 TransformPassKind::SelectorMerging,
981 TransformPassKind::NestingUnwrap,
982 TransformPassKind::ScopeFlatten,
983 TransformPassKind::LayerFlatten,
984 ]
985 .into_iter()
986 .map(TransformPassKind::id)
987 .collect::<Vec<_>>();
988
989 assert_eq!(profile.profile_id, STRICT_VERIFICATION_BUILD_PROFILE_ID_V0);
990 assert_eq!(profile.pass_ids, expected);
991 assert!(
992 !profile
993 .pass_ids
994 .contains(&TransformPassKind::EmptyRuleRemoval.id())
995 );
996 assert_eq!(policy.profile_id, profile.profile_id);
997 assert!(policy.refuse_unknown_pass_ids);
998 assert!(policy.require_closed_world_evidence);
999 assert!(policy.require_complete_decisions);
1000 assert!(policy.enforce_winner_equality);
1001 }
1002}