export * from "./BooleanExpression" ; export * from "./StyleMapping" ; export * from "./TemplateError" ; export * from "./TemplateIntegrationOptions" ; export interface TemplateTypes { "Opticss.Template" : Template ; } /**
 * This type is used to serialize arbitrary template info instances to JSON and back.
 */ export interface SerializedTemplateInfo < K extends keyof TemplateTypes > { /** This is the type string for the template info class as it's registered with TemplateInfoFactory. */ type : K ; /**
     * any identifier that can be used to look up a template by the TemplateInfo.
     * Usually a relative path to a file.
     */ identifier : string ; /** the values stored in here must be JSON-friendly. */ data ? : unknown [ ] ; } export interface TemplateInfo < K extends keyof TemplateTypes > { /**
     * The string under which the template is registered with the TemplateFactory.
     */ type : K ; /**
     * Any identifier that can be used to look up a template by the TemplateInfo.
     * Usually a path to a file.
     */ identifier : string ; serialize ( ) : SerializedTemplateInfo < K > ; } export type TemplateConstructors < T extends TemplateTypes = TemplateTypes > = { [ P in keyof T ] ? : ( identifier : string , ... _data : unknown [ ] ) => T [ P ] ; } ; /**
 * Subclasses of TemplateInfo must be registered onto the static class factory.
 * it is important for the registered name of the template info to be unique
 * from all other possible names for other types of template info.
 *
 * A function that accepts serialized data and returns an instance of the type
 * must be registered so that serializable data types that have references to
 * templates can transparently serialize and deserialize them.
 *
 * Note that when using TypeScript, the TemplateTypes interface must first have your key and template type added to it
 * before attempting to register the deserializer function.
 *
 * import { TemplateTypes, TemplateInfo, SerializedTemplateInfo, TemplateInfoFactory } from "@opticss/template-api"
 * declare module "@opticss/template-api" {
 *   export interface TemplateTypes {
 *     "MyTemplateTypeName": MyTemplateClass;
 *   }
 * }
 *
 * class MyTemplateClass implements TemplateInfo<"MyTemplateTypeName"> {
 *   static deserialize(identifier: string, ...data: any[]): MyTemplateClass {
 *     return new MyTemplateClass(identifier);
 *   }
 *   serialize(): SerializedTemplateInfo<"MyTemplateTypeName"> {
 *     return { ... };
 *   }
 * }
 * TemplateInfoFactory.constructors["MyTemplateTypeName"] = MyTemplateClass.deserialize;
 */ export class TemplateInfoFactory { static constructors : TemplateConstructors = { } ; static create < K extends keyof TemplateTypes > ( name , identifier , ... data ) { let constructor = TemplateInfoFactory . constructors [ name ] ; if ( constructor !== undefined ) { return constructor ( identifier , ... data ) ; } else { throw new Error ( `No template info registered for ${ name }` ) ; } } static deserialize < K extends keyof TemplateTypes > ( obj ) { let data = obj . data || [ ] ; return TemplateInfoFactory . create ( obj . type , obj . identifier , ... data ) ; } } export function isTemplateType < K extends keyof TemplateTypes > ( type , template ) ; export function isTemplateType < K extends keyof TemplateTypes > ( type , template ) ; export function isTemplateType < K extends keyof TemplateTypes > ( type , template ) { return ( template . type === type ) ; } /**
 * Base class for template information for an analyzed template.
 */ export class Template implements TemplateInfo < "Opticss.Template" > { type : "Opticss.Template" ; identifier : string ; constructor ( identifier ) { this . identifier = identifier ; this . type = "Opticss.Template" ; } static deserialize ( identifier , ... _data ) { return new Template ( identifier ) ; } serialize ( ) { return { type : this . type , identifier : this . identifier , } ; } } TemplateInfoFactory . constructors [ "Opticss.Template" ] = Template . deserialize ; import { Attr , AttributeBase , Element , POSITION_UNKNOWN , SerializedElementInfo , SourceLocation , SourcePosition , Tag , TagnameBase , } from "@opticss/element-analysis" ; import { ObjectDictionary , OptiCSSError } from "@opticss/util" ; export interface SerializedTemplateAnalysis < K extends keyof TemplateTypes > { template : SerializedTemplateInfo < K > ; elements : SerializedElementInfo [ ] ; } /**
 * A TemplateAnalysis tracks style-relevant markup information on elements. The
 * goal of this analysis is to provide information necessary to prove that two
 * selectors that set the same css property but to different values never
 * target the same html element. However, it's very hard to prove a negative,
 * so instead we use the analysis to decide whether there is an element that
 * two conflicting selectors may match. If no such element is found in the
 * analysis, we assume it doesn't exist.
 *
 * This class can be used while traversing a document or template AST to record
 * the style-relevant markup information.
 *
 * It also provides efficient querying to decide if two selectors' key
 * selector might match the same element. This analysis information makes no
 * attempt at recording the hierarchical information of a document. Hierarchy
 * information is deemed unreliable to determine statically. We may revisit
 * hierarchical analysis in the future.
 *
 * 1. Call [[startElement startElement(tagname)]] at the beginning of an new html element.
 * 2. Call [[addAttribute addAttribute(attribute)]] for all the style-relevant attributes used on the current html element.
 * 3. Call [[endElement endElement()]] when done adding attributes for the current element.
 */ export class TemplateAnalysis < K extends keyof TemplateTypes > { template : TemplateTypes [ K ] ; /**
     * A list of all the styles that are used together on the same element.
     * The current correlation is added to this list when [[endElement]] is called.
     */ elements : Element [ ] ; /**
     * The current element is created when calling [[startElement]].
     * The current element is unset after calling [[endElement]].
     */ currentElement ? : Element ; param template constructor ( template ) { this . template = template ; this . elements = [ ] ; } /**
     * Indicates a new element found in a template.
     *
     * If for some reason the code can't know the source position,
     * you should pass the `POSITION_UNKNOWN` constant value.
     *
     * Always call [[endElement]] before calling the next [[startElement]],
     * even if the elements are nested in the document.
     */ startElement ( tagname , position ) { if ( this . currentElement ) { throw new OptiCSSError ( `endElement wasn't called after a previous call to startElement` , { filename : this . template . identifier , ... position } ) ; } let startPos = ( position . line >= 0 ) ? { start : position } : undefined ; this . currentElement = new Element ( tagname , new Array < Attr > ( ) , startPos ) ; return this ; } setId ( id ) { if ( this . currentElement ) { this . currentElement . id = id ; } else { throw new OptiCSSError ( `startElement() must be called before calling setId()` , { filename : this . template . identifier } ) ; } return this ; } getElement ( id ) { return this . elements . find ( el => el . id === id ) ; } /**
     * Add an attribute. Dynamic values are handled according to the value that is
     * given to the attribute. For instance imagine an element:
     * `<div class="class1 $foo class3 $($condition ? 'class4' : 'class5') size-$size">`
     *
     * Depending on what the analyzer can deduce about the dynamic values this could
     * end up represented by several different values.
     *
     * If the analyzer has no idea what `$foo` might be, then the value for this
     * attribute should simply be `{unknown: true}` because if any number of
     * class could be returned then there's no benefit to providing information
     * about the other classes from the optimizer's perspective. If however,
     * there's a way of knowing that the unknown value is limited to a single
     * identifier, then there is some marginal value in recording it using the
     * `{unknownIdentifier: true}` value -- especially if it's used in a way where
     * it would be the only class, that's enough information to know that two
     * different classes won't conflict.
     *
     * Let's assume that the analyzer was able to determine that $foo is a class `class2`
     * but that sometimes it's not set. In this case we'd set the value to:
     *
     * ```
     * {
     *   allOf: [
     *     {value: "class1"},    // class1
     *     {oneOf: [             // $foo
     *       {absent: true},
     *       {value: "class2"}
     *     ]}
     *     {value: "class3"},    // class3
     *     {oneOf: [             // $($condition ? 'class4' : 'class5')
     *       {value: "class4"},
     *       {value: "class5"}
     *     ]}
     *     {startsWith: "size-", // size-$size
     *      whitespace: false}
     *   ]
     * }
     * ```
     *
     * Especially for the class attribute, even one html element with an unknown
     * value or two unknown identifiers will have detrimental effects on the
     * ability to optimize the entire stylesheet.
     */ addAttribute ( attr ) { if ( this . currentElement ) { this . currentElement . attributes . push ( attr ) ; } else { throw new OptiCSSError ( `startElement() must be called before calling addAttribute()` , { filename : this . template . identifier } ) ; } return this ; } /**
     * Indicates all styles for the element have been found.
     *
     * If for some reason the code can't know the source position,
     * you should pass the `POSITION_UNKNOWN` constant value.
     *
     */ endElement ( position ? ) { if ( this . currentElement ) { if ( this . currentElement . sourceLocation && position && position . line >= 0 ) { this . currentElement . sourceLocation . end = position ; } this . elements . push ( this . currentElement ) ; this . currentElement = undefined ; } return this ; } constants ( attrTypes ) { let found = { } ; attrTypes . forEach ( k => { found [ k ] = new Set < string > ( ) ; } ) ; for ( let element of this . elements ) { for ( let attr of element . attributes ) { if ( attrTypes . has ( attr . name ) ) { for ( let constant of attr . constants ( ) ) { found [ attr . name ] . add ( constant ) ; } } } } return found ; } /**
     * Generates a [[SerializedTemplateAnalysis]] for this analysis.
     */ serialize ( ) { let elements = new Array < SerializedElementInfo > ( ) ; this . elements . forEach ( ( element ) => { elements . push ( element . serialize ( ) ) ; } ) ; return { template : this . template . serialize ( ) as SerializedTemplateInfo < K > , elements , } ; } param serializedAnalysis param options param postcssImpl static deserialize < K extends keyof TemplateTypes > ( serializedAnalysis ) { let template = TemplateInfoFactory . deserialize < K > ( serializedAnalysis . template ) ; let analysis = new TemplateAnalysis < K > ( template ) ; serializedAnalysis . elements . forEach ( element => { analysis . startElement ( TagnameBase . fromJSON ( element . tagname ) , element . sourceLocation && element . sourceLocation . start || POSITION_UNKNOWN ) ; element . attributes . forEach ( attribute => { analysis . addAttribute ( AttributeBase . fromJSON ( attribute ) ) ; } ) ; analysis . endElement ( element . sourceLocation && element . sourceLocation . end || POSITION_UNKNOWN ) ; } ) ; return analysis ; } }