// @flow

type Exclude<A, B> = A;
// see https://gist.github.com/thecotne/6e5969f4aaf8f253985ed36b30ac9fe0
type $FlowGen$If<X: boolean, Then, Else = empty> = $Call<
  ((true, Then, Else) => Then) & ((false, Then, Else) => Else),
  X,
  Then,
  Else
>;

type $FlowGen$Assignable<A, B> = $Call<
  ((...r: [B]) => true) & ((...r: [A]) => false),
  A
>;

import type {
  Angle,
  CssColor,
  Rule,
  CustomProperty,
  EnvironmentVariable,
  Function,
  Image,
  LengthValue,
  MediaQuery,
  Declaration,
  Ratio,
  Resolution,
  Selector,
  SupportsCondition,
  Time,
  Token,
  TokenOrValue,
  UnknownAtRule,
  Url,
  Variable,
  StyleRule,
  DeclarationBlock,
  ParsedComponent,
  Multiplier,
  StyleSheet,
  Location2,
} from "./ast.js.flow";
import { Targets, Features } from "./targets.js.flow";
declare export * from "./ast.js.flow";
declare export { Targets, Features };
export type TransformOptions<C: CustomAtRules> = {|
  /**
   * The filename being transformed. Used for error messages and source maps.
   */
  filename: string,

  /**
   * The source code to transform.
   */
  code: Uint8Array,

  /**
   * Whether to enable minification.
   */
  minify?: boolean,

  /**
   * Whether to output a source map.
   */
  sourceMap?: boolean,

  /**
   * An input source map to extend.
   */
  inputSourceMap?: string,

  /**
   * An optional project root path, used as the source root in the output source map.
   * Also used to generate relative paths for sources used in CSS module hashes.
   */
  projectRoot?: string,

  /**
   * The browser targets for the generated code.
   */
  targets?: Targets,

  /**
   * Features that should always be compiled, even when supported by targets.
   */
  include?: number,

  /**
   * Features that should never be compiled, even when unsupported by targets.
   */
  exclude?: number,

  /**
   * Whether to enable parsing various draft syntax.
   */
  drafts?: Drafts,

  /**
   * Whether to enable various non-standard syntax.
   */
  nonStandard?: NonStandard,

  /**
   * Whether to compile this file as a CSS module.
   */
  cssModules?: boolean | CSSModulesConfig,

  /**
   * Whether to analyze dependencies (e.g. string).
   * When enabled, string dependencies
   * are replaced with hashed placeholders that can be replaced with the final
   * urls later (after bundling). Dependencies are returned as part of the result.
   */
  analyzeDependencies?: boolean | DependencyOptions,

  /**
   * Replaces user action pseudo classes with class names that can be applied from JavaScript.
   * This is useful for polyfills, for example.
   */
  pseudoClasses?: PseudoClasses,

  /**
   * A list of class names, ids, and custom identifiers (e.g. @keyframes) that are known
   * to be unused. These will be removed during minification. Note that these are not
   * selectors but individual names (without any . or # prefixes).
   */
  unusedSymbols?: string[],

  /**
   * Whether to ignore invalid rules and declarations rather than erroring.
   * When enabled, warnings are returned, and the invalid rule or declaration is
   * omitted from the output code.
   */
  errorRecovery?: boolean,

  /**
   * An AST visitor object. This allows custom transforms or analysis to be implemented in JavaScript.
   * Multiple visitors can be composed into one using the string function.
   * For optimal performance, visitors should be as specific as possible about what types of values
   * they care about so that JavaScript has to be called as little as possible.
   */
  visitor?: Visitor<C>,

  /**
   * Defines how to parse custom CSS at-rules. Each at-rule can have a prelude, defined using a CSS
   * [syntax string](https://drafts.css-houdini.org/css-properties-values-api/#syntax-strings), and
   * a block body. The body can be a declaration list, rule list, or style block as defined in the
   * [css spec](https://drafts.csswg.org/css-syntax/#declaration-rule-list).
   */
  customAtRules?: C,
|};
declare type PropertyStart =
  | "-"
  | "_"
  | "a"
  | "b"
  | "c"
  | "d"
  | "e"
  | "f"
  | "g"
  | "h"
  | "i"
  | "j"
  | "k"
  | "l"
  | "m"
  | "n"
  | "o"
  | "p"
  | "q"
  | "r"
  | "s"
  | "t"
  | "u"
  | "v"
  | "w"
  | "x"
  | "y"
  | "z";
export type ReturnedDeclaration =
  | Declaration
  | {|
      /**
       * The property name.
       */
      property: string,

      /**
       * The raw string value for the declaration.
       */
      raw: string,
    |};
export type ReturnedMediaQuery =
  | MediaQuery
  | {|
      /**
       * The raw string value for the media query.
       */
      raw: string,
    |};
declare type FindByType<Union, Name> = $FlowGen$If<
  $FlowGen$Assignable<
    Union,
    {|
      type: Name,
    |}
  >,
  Union,
  empty
>;
export type ReturnedRule = Rule<ReturnedDeclaration, ReturnedMediaQuery>;
declare type RequiredValue<Rule> = $FlowGen$If<
  $FlowGen$Assignable<
    Rule,
    {|
      value: { [key: string]: any },
    |}
  >,
  $FlowGen$If<
    $FlowGen$Assignable<$PropertyType<Rule, "value">, StyleRule>,
    {|
      ...Rule,
      ...{|
        value: {|
          ...Required<StyleRule>,
          ...{|
            declarations: Required<DeclarationBlock>,
          |},
        |},
      |},
    |},
    {|
      ...Rule,
      ...{|
        value: Required<$PropertyType<Rule, "value">>,
      |},
    |}
  >,
  Rule
>;
declare type RuleVisitor<R = RequiredValue<Rule>> = (
  rule: R
) => ReturnedRule | ReturnedRule[] | void;
declare type MappedRuleVisitors = $ObjMapi<
  { [k: Exclude<$PropertyType<Rule, "type">, "unknown" | "custom">]: any },
  <Name>(Name) => RuleVisitor<RequiredValue<FindByType<Rule, Name>>>
>;
declare type UnknownVisitors<T> = {
  [name: string]: RuleVisitor<T>,
};
declare type CustomVisitors<T: CustomAtRules> = $ObjMapi<
  T,
  <Name>(Name) => RuleVisitor<CustomAtRule<Name, $ElementType<T, Name>>>
>;
declare type AnyCustomAtRule<C: CustomAtRules> = $ElementType<
  $ObjMapi<C, <Key>(Key) => CustomAtRule<Key, $ElementType<C, Key>>>,
  $Keys<C>
>;
declare type RuleVisitors<C: CustomAtRules> = {|
  ...MappedRuleVisitors,
  ...{|
    unknown?:
      | UnknownVisitors<UnknownAtRule>
      | $Diff<
          RuleVisitor<UnknownAtRule>,
          { [key: $Keys<CallableFunction>]: any }
        >,
    custom?:
      | CustomVisitors<C>
      | $Diff<
          RuleVisitor<AnyCustomAtRule<C>>,
          { [key: $Keys<CallableFunction>]: any }
        >,
  |},
|};
declare type PreludeTypes = Exclude<
  $PropertyType<ParsedComponent, "type">,
  "literal" | "repeated" | "token"
>;
declare type SyntaxString = string | string;
declare type ComponentTypes = $ObjMapi<
  { [k: PreludeTypes]: any },
  <Key>(Key) => FindByType<ParsedComponent, Key>
>;
declare type Repetitions = $ObjMapi<
  { [k: PreludeTypes]: any },
  <Key>(Key) => {|
    type: "repeated",
    value: {|
      components: FindByType<ParsedComponent, Key>[],
      multiplier: Multiplier,
    |},
  |}
>;
declare type MappedPrelude = {| ...ComponentTypes, ...Repetitions |};
declare type MappedBody<P: $PropertyType<CustomAtRuleDefinition, "body">> =
  $FlowGen$If<$FlowGen$Assignable<P, "style-block">, "rule-list", P>;
declare type CustomAtRule<N, R: CustomAtRuleDefinition> = {|
  name: N,
  prelude: $FlowGen$If<
    $FlowGen$Assignable<$PropertyType<R, "prelude">, $Keys<MappedPrelude>>,
    $ElementType<MappedPrelude, $PropertyType<R, "prelude">>,
    ParsedComponent
  >,
  body: FindByType<CustomAtRuleBody, MappedBody<$PropertyType<R, "body">>>,
  loc: Location2,
|};
declare type CustomAtRuleBody =
  | {|
      type: "declaration-list",
      value: Required<DeclarationBlock>,
    |}
  | {|
      type: "rule-list",
      value: RequiredValue<Rule>[],
    |};
declare type FindProperty<Union, Name> = $FlowGen$If<
  $FlowGen$Assignable<
    Union,
    {|
      property: Name,
    |}
  >,
  Union,
  empty
>;
declare type DeclarationVisitor<P = Declaration> = (
  property: P
) => ReturnedDeclaration | ReturnedDeclaration[] | void;
declare type MappedDeclarationVisitors = $ObjMapi<
  {
    [k: Exclude<
      $PropertyType<Declaration, "property">,
      "unparsed" | "custom"
    >]: any,
  },
  <Name>(
    Name
  ) => DeclarationVisitor<
    FindProperty<Declaration, Name> | FindProperty<Declaration, "unparsed">
  >
>;
declare type CustomPropertyVisitors = {
  [name: string]: DeclarationVisitor<CustomProperty>,
};
declare type DeclarationVisitors = {|
  ...MappedDeclarationVisitors,
  ...{|
    custom?: CustomPropertyVisitors | DeclarationVisitor<CustomProperty>,
  |},
|};
declare type RawValue = {|
  /**
   * A raw string value which will be parsed like CSS.
   */
  raw: string,
|};
declare type TokenReturnValue = TokenOrValue | TokenOrValue[] | RawValue | void;
declare type TokenVisitor = (token: Token) => TokenReturnValue;
declare type VisitableTokenTypes =
  | "ident"
  | "at-keyword"
  | "hash"
  | "id-hash"
  | "string"
  | "number"
  | "percentage"
  | "dimension";
declare type TokenVisitors = $ObjMapi<
  { [k: VisitableTokenTypes]: any },
  <Name>(Name) => (token: FindByType<Token, Name>) => TokenReturnValue
>;
declare type FunctionVisitor = (fn: Function) => TokenReturnValue;
declare type EnvironmentVariableVisitor = (
  env: EnvironmentVariable
) => TokenReturnValue;
declare type EnvironmentVariableVisitors = {
  [name: string]: EnvironmentVariableVisitor,
};
export type Visitor<C: CustomAtRules> = {|
  StyleSheet?: (
    stylesheet: StyleSheet
  ) => StyleSheet<ReturnedDeclaration, ReturnedMediaQuery> | void,
  StyleSheetExit?: (
    stylesheet: StyleSheet
  ) => StyleSheet<ReturnedDeclaration, ReturnedMediaQuery> | void,
  Rule?: RuleVisitor<> | RuleVisitors<C>,
  RuleExit?: RuleVisitor<> | RuleVisitors<C>,
  Declaration?: DeclarationVisitor<> | DeclarationVisitors,
  DeclarationExit?: DeclarationVisitor<> | DeclarationVisitors,
  Url?: (url: Url) => Url | void,
  Color?: (color: CssColor) => CssColor | void,
  Image?: (image: Image) => Image | void,
  ImageExit?: (image: Image) => Image | void,
  Length?: (length: LengthValue) => LengthValue | void,
  Angle?: (angle: Angle) => Angle | void,
  Ratio?: (ratio: Ratio) => Ratio | void,
  Resolution?: (resolution: Resolution) => Resolution | void,
  Time?: (time: Time) => Time | void,
  CustomIdent?: (ident: string) => string | void,
  DashedIdent?: (ident: string) => string | void,
  MediaQuery?: (
    query: MediaQuery
  ) => ReturnedMediaQuery | ReturnedMediaQuery[] | void,
  MediaQueryExit?: (
    query: MediaQuery
  ) => ReturnedMediaQuery | ReturnedMediaQuery[] | void,
  SupportsCondition?: (condition: SupportsCondition) => SupportsCondition,
  SupportsConditionExit?: (condition: SupportsCondition) => SupportsCondition,
  Selector?: (selector: Selector) => Selector | Selector[] | void,
  Token?: TokenVisitor | TokenVisitors,
  Function?:
    | FunctionVisitor
    | {
        [name: string]: FunctionVisitor,
      },
  FunctionExit?:
    | FunctionVisitor
    | {
        [name: string]: FunctionVisitor,
      },
  Variable?: (variable: Variable) => TokenReturnValue,
  VariableExit?: (variable: Variable) => TokenReturnValue,
  EnvironmentVariable?:
    | EnvironmentVariableVisitor
    | EnvironmentVariableVisitors,
  EnvironmentVariableExit?:
    | EnvironmentVariableVisitor
    | EnvironmentVariableVisitors,
|};
export type CustomAtRules = {|
  [name: string]: CustomAtRuleDefinition,
|};
export type CustomAtRuleDefinition = {|
  /**
   * Defines the syntax for a custom at-rule prelude. The value should be a
   * CSS [syntax string](https://drafts.css-houdini.org/css-properties-values-api/#syntax-strings)
   * representing the types of values that are accepted. This property may be omitted or
   * set to null to indicate that no prelude is accepted.
   */
  prelude?: SyntaxString | null,

  /**
   * Defines the type of body contained within the at-rule block.
   *   - declaration-list: A CSS declaration list, as in a style rule.
   *   - rule-list: A list of CSS rules, as supported within a non-nested
   *       at-rule such as string.
   *   - style-block: Both a declaration list and rule list, as accepted within
   *       a nested at-rule within a style rule (e.g. string inside a style rule
   *       with directly nested declarations).
   */
  body?: "declaration-list" | "rule-list" | "style-block" | null,
|};
export type DependencyOptions = {|
  /**
   * Whether to preserve string rules rather than removing them.
   */
  preserveImports?: boolean,
|};
export type BundleOptions<C: CustomAtRules> = $Diff<
  TransformOptions<C>,
  {| code: any |}
>;
export type BundleAsyncOptions<C: CustomAtRules> = {|
  ...$Exact<BundleOptions<C>>,

  resolver?: Resolver,
|};

/**
 * Custom resolver to use when loading CSS files.
 */
export type Resolver = {|
  /**
   * Read the given file and return its contents as a string.
   */
  read?: (file: string) => string | Promise<string>,

  /**
   * Resolve the given CSS import specifier from the provided originating file to a
   * path which gets passed to string.
   */
  resolve?: (
    specifier: string,
    originatingFile: string
  ) => string | Promise<string>,
|};
export type Drafts = {|
  /**
   * Whether to enable @custom-media rules.
   */
  customMedia?: boolean,
|};
export type NonStandard = {|
  /**
   * Whether to enable the non-standard >>> and /deep/ selector combinators used by Angular and Vue.
   */
  deepSelectorCombinator?: boolean,
|};
export type PseudoClasses = {|
  hover?: string,
  active?: string,
  focus?: string,
  focusVisible?: string,
  focusWithin?: string,
|};
export type TransformResult = {|
  /**
   * The transformed code.
   */
  code: Uint8Array,

  /**
   * The generated source map, if enabled.
   */
  map: Uint8Array | void,

  /**
   * CSS module exports, if enabled.
   */
  exports: CSSModuleExports | void,

  /**
   * CSS module references, if string is enabled.
   */
  references: CSSModuleReferences,

  /**
   * string dependencies, if enabled.
   */
  dependencies: Dependency[] | void,

  /**
   * Warnings that occurred during compilation.
   */
  warnings: Warning[],
|};
export type Warning = {|
  message: string,
  type: string,
  value?: any,
  loc: ErrorLocation,
|};
export type CSSModulesConfig = {|
  /**
   * The pattern to use when renaming class names and other identifiers. Default is string.
   */
  pattern?: string,

  /**
   * Whether to rename dashed identifiers, e.g. custom properties.
   */
  dashedIdents?: boolean,

  /**
   * Whether to enable hashing for string.
   */
  animation?: boolean,

  /**
   * Whether to enable hashing for CSS grid identifiers.
   */
  grid?: boolean,

  /**
   * Whether to enable hashing for string names.
   */
  container?: boolean,

  /**
   * Whether to enable hashing for custom identifiers.
   */
  customIdents?: boolean,

  /**
   * Whether to require at least one class or id selector in each rule.
   */
  pure?: boolean,
|};
export type CSSModuleExports = {
  /**
   * Maps exported (i.e. original) names to local names.
   */
  [name: string]: CSSModuleExport,
};
export type CSSModuleExport = {|
  /**
   * The local (compiled) name for this export.
   */
  name: string,

  /**
   * Whether the export is referenced in this file.
   */
  isReferenced: boolean,

  /**
   * Other names that are composed by this export.
   */
  composes: CSSModuleReference[],
|};
export type CSSModuleReferences = {
  /**
   * Maps placeholder names to references.
   */
  [name: string]: DependencyCSSModuleReference,
};
export type CSSModuleReference =
  | LocalCSSModuleReference
  | GlobalCSSModuleReference
  | DependencyCSSModuleReference;
export type LocalCSSModuleReference = {|
  type: "local",

  /**
   * The local (compiled) name for the reference.
   */
  name: string,
|};
export type GlobalCSSModuleReference = {|
  type: "global",

  /**
   * The referenced global name.
   */
  name: string,
|};
export type DependencyCSSModuleReference = {|
  type: "dependency",

  /**
   * The name to reference within the dependency.
   */
  name: string,

  /**
   * The dependency specifier for the referenced file.
   */
  specifier: string,
|};
export type Dependency = ImportDependency | UrlDependency;
export type ImportDependency = {|
  type: "import",

  /**
   * The url of the string dependency.
   */
  url: string,

  /**
   * The media query for the string rule.
   */
  media: string | null,

  /**
   * The string rule.
   */
  supports: string | null,

  /**
   * The source location where the string rule was found.
   */
  loc: SourceLocation,

  /**
   * The placeholder that the import was replaced with.
   */
  placeholder: string,
|};
export type UrlDependency = {|
  type: "url",

  /**
   * The url of the dependency.
   */
  url: string,

  /**
   * The source location where the string was found.
   */
  loc: SourceLocation,

  /**
   * The placeholder that the url was replaced with.
   */
  placeholder: string,
|};
export type SourceLocation = {|
  /**
   * The file path in which the dependency exists.
   */
  filePath: string,

  /**
   * The start location of the dependency.
   */
  start: Location,

  /**
   * The end location (inclusive) of the dependency.
   */
  end: Location,
|};
export type Location = {|
  /**
   * The line number (1-based).
   */
  line: number,

  /**
   * The column number (0-based).
   */
  column: number,
|};
export type ErrorLocation = {|
  ...$Exact<Location>,

  filename: string,
|};

/**
 * Compiles a CSS file, including optionally minifying and lowering syntax to the given
 * targets. A source map may also be generated, but this is not enabled by default.
 */
declare export function transform<C: CustomAtRules>(
  options: TransformOptions<C>
): TransformResult;
export type TransformAttributeOptions = {|
  /**
   * The filename in which the style attribute appeared. Used for error messages and dependencies.
   */
  filename?: string,

  /**
   * The source code to transform.
   */
  code: Uint8Array,

  /**
   * Whether to enable minification.
   */
  minify?: boolean,

  /**
   * The browser targets for the generated code.
   */
  targets?: Targets,

  /**
   * Whether to analyze string dependencies.
   * When enabled, string dependencies are replaced with hashed placeholders
   * that can be replaced with the final urls later (after bundling).
   * Dependencies are returned as part of the result.
   */
  analyzeDependencies?: boolean,

  /**
   * Whether to ignore invalid rules and declarations rather than erroring.
   * When enabled, warnings are returned, and the invalid rule or declaration is
   * omitted from the output code.
   */
  errorRecovery?: boolean,

  /**
   * An AST visitor object. This allows custom transforms or analysis to be implemented in JavaScript.
   * Multiple visitors can be composed into one using the string function.
   * For optimal performance, visitors should be as specific as possible about what types of values
   * they care about so that JavaScript has to be called as little as possible.
   */
  visitor?: Visitor<empty>,
|};
export type TransformAttributeResult = {|
  /**
   * The transformed code.
   */
  code: Uint8Array,

  /**
   * string dependencies, if enabled.
   */
  dependencies: Dependency[] | void,

  /**
   * Warnings that occurred during compilation.
   */
  warnings: Warning[],
|};

/**
 * Compiles a single CSS declaration list, such as an inline style attribute in HTML.
 */
declare export function transformStyleAttribute(
  options: TransformAttributeOptions
): TransformAttributeResult;

/**
 * Converts a browserslist result into targets that can be passed to lightningcss.
 * @param browserslist the result of calling string
 */
declare export function browserslistToTargets(browserslist: string[]): Targets;

/**
 * Bundles a CSS file and its dependencies, inlining @import rules.
 */
declare export function bundle<C: CustomAtRules>(
  options: BundleOptions<C>
): TransformResult;

/**
 * Bundles a CSS file and its dependencies asynchronously, inlining @import rules.
 */
declare export function bundleAsync<C: CustomAtRules>(
  options: BundleAsyncOptions<C>
): Promise<TransformResult>;

/**
 * Composes multiple visitor objects into a single one.
 */
declare export function composeVisitors<C: CustomAtRules>(
  visitors: Visitor<C>[]
): Visitor<C>;
