# `Noizu.Tessera.FromLit.Extractor`
[🔗](https://github.com/the-robot-lives/tessera/blob/v0.2.0/lib/noizu/tessera/from_lit/extractor.ex#L1)

Mechanical shape extraction from a Lit 3 component source (`.js`/`.ts`).

Best-effort regex/scanner parsing — deliberately NOT a JavaScript parser.
It recognizes the common Lit 3 declaration patterns and returns a plain
JSON-able shape descriptor that `Noizu.Tessera.FromLit.Generator` turns
into a tessera spec stub. Anything it cannot parse mechanically is surfaced
in `"warnings"` — never silently dropped (a conversion tool that lies is
worse than none).

Supported patterns:

  * `@customElement("tag")` and `customElements.define("tag", Class)`;
  * `@property({...})` / `@state()` decorated fields (multiline options,
    TS annotations, `declare`/`accessor`/visibility modifiers, literal
    defaults);
  * `static properties = {...}` and `static get properties() { return {...} }`
    (option objects and `name: Type` shorthand, `state: true` entries);
  * `this.dispatchEvent(new CustomEvent("name", {detail: {...}, bubbles,
    composed}))` and `new Event("name")` — detail key types are inferred
    from literal values, non-literal values become `"any"`;
  * `<slot>` / `<slot name="...">` (unnamed slots become `"default"`);
  * `static styles = css\`...\`` (single, array, or getter form) — raw CSS
    text is captured; `${...}` interpolation is flagged, not resolved.

Known limitations (warned, not guessed): computed defaults, custom
converters / `hasChanged`, spread options, non-literal event names,
interpolated styles, events constructed away from their `dispatchEvent`
call, and templates nested inside `${...}` interpolations.

Shape descriptor (string keys, JSON-encodable):

    %{
      "source" => path | nil,
      "tag" => "my-element" | nil,
      "class_name" => "MyElement" | nil,
      "properties" => [
        %{"name" => "displayName", "kind" => "property" | "state" | "static",
          "type" => "string" | "integer" | "float" | "boolean" | "list" | "map" | "any",
          "source_type" => "String" | "number" | nil,
          "default" => literal | nil, "default_expression" => raw | nil,
          "attribute" => true | false | "custom-name", "reflect" => boolean}
      ],
      "events" => [
        %{"name" => "persona-select",
          "detail" => [%{"name" => "personaId", "type" => "string"}],
          "bubbles" => boolean, "composed" => boolean}
      ],
      "slots" => [%{"name" => "header"}],
      "styles" => ["raw css", ...],
      "warnings" => ["..."]
    }

# `shape`

```elixir
@type shape() :: map()
```

# `extract`

```elixir
@spec extract(String.t(), keyword()) :: shape()
```

Extracts the shape descriptor from a Lit component source string. `opts`:
`:path` — the source path recorded in `"source"` and used in warnings.

# `extract_file`

```elixir
@spec extract_file(Path.t()) :: {:ok, shape()} | {:error, term()}
```

Reads a file and extracts its shape. Returns `{:ok, shape}` or `{:error, reason}`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
