Packages
phoenix_kit_ecommerce
0.5.21
0.5.24
0.5.23
0.5.22
0.5.21
0.5.20
0.5.19
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.3
0.4.2
0.4.0
0.3.0
0.2.2
0.2.1
0.2.0
0.1.16
0.1.15
0.1.14
0.1.13
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
E-commerce module for PhoenixKit — products, categories, cart, checkout
Current section
Files
Jump to
Current section
Files
phoenix_kit_ecommerce
mix.exs
mix.exs
defmodule PhoenixKitEcommerce.MixProject do
use Mix.Project
@version "0.5.21"
@source_url "https://github.com/BeamLabEU/phoenix_kit_ecommerce"
def project do
[
app: :phoenix_kit_ecommerce,
version: @version,
elixir: "~> 1.18",
elixirc_paths: elixirc_paths(Mix.env()),
# `compat/shop.ex` intentionally redefines the core
# `PhoenixKit.Modules.Shop` namespace during the transition to the
# `PhoenixKitEcommerce` namespace, which triggers a module-redefinition
# warning that `--warnings-as-errors` would otherwise fail on.
# Removal condition: drop this once core no longer ships the old
# `PhoenixKit.Modules.Shop` namespace (then delete compat/shop.ex too).
elixirc_options: [ignore_module_conflict: true],
start_permanent: Mix.env() == :prod,
deps: deps(),
aliases: aliases(),
test_ignore_filters: [~r"/support/"],
test_coverage: [
ignore_modules: [
~r/^PhoenixKitEcommerce\.Test\./,
PhoenixKitEcommerce.DataCase,
PhoenixKitEcommerce.LiveCase,
PhoenixKitEcommerce.ActivityLogAssertions
]
],
# Hex
description: "E-commerce module for PhoenixKit — products, categories, cart, checkout",
package: package(),
# Dialyzer
dialyzer: [plt_add_apps: [:phoenix_kit, :phoenix_kit_billing, :mix]],
# Docs
name: "PhoenixKitEcommerce",
source_url: @source_url,
docs: docs()
]
end
def application do
[extra_applications: [:logger, :gettext]]
end
def cli do
[preferred_envs: ["test.setup": :test, "test.reset": :test]]
end
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]
defp aliases do
[
quality: ["format", "credo --strict", "dialyzer"],
"quality.ci": ["format --check-formatted", "credo --strict", "dialyzer"],
precommit: [
"compile --force --warnings-as-errors",
"deps.unlock --check-unused",
# Scan for retired Hex deps. Run via `cmd` so Hex bootstraps in a fresh
# process — the hex.* archive tasks aren't resolvable via Mix.Task.run
# inside an alias.
"cmd mix hex.audit",
"quality.ci"
],
"test.setup": [
"ecto.create --quiet -r PhoenixKitEcommerce.Test.Repo"
],
"test.reset": [
"ecto.drop --quiet -r PhoenixKitEcommerce.Test.Repo",
"test.setup"
]
]
end
# phoenix_kit deps resolve from Hex by default. For cross-repo work against a
# local checkout, export <APP>_PATH — e.g. PHOENIX_KIT_PATH=../phoenix_kit or
# PHOENIX_KIT_AI_PATH=../phoenix_kit_ai. Unset => the published pin, so
# mix hex.publish is unaffected.
defp pk_dep(app, requirement, opts \\ []) do
env_var = String.upcase(Atom.to_string(app)) <> "_PATH"
# A set-but-blank value (`PHOENIX_KIT_PATH=`) reads as "" rather than nil;
# treat it as unset so it falls back to the Hex pin instead of building a
# broken `path: ""` dep.
case System.get_env(env_var) do
path when is_binary(path) and path != "" ->
{app, [path: path, override: true] ++ opts}
_unset when opts == [] ->
{app, requirement}
_unset ->
{app, requirement, opts}
end
end
defp deps do
[
# The floor is 2.43.0: `ImageFingerprint` runs ImageMagick through
# core's `ImageProcessor.limit_args/0` and `pinned_input/2` (resource
# limits, decoder pinned to the sniffed format), first shipped there.
# That subsumes 2.38.0 — the actor and the activity log
# (`PhoenixKitWeb.Actor`, `PhoenixKit.Activity.log/3`), not
# feature-detected, so a lower core fails to compile.
# Everything the earlier floors guaranteed is subsumed: V186's
# `base_currency`/`exchange_rate`/`base_unit_price` cart columns (2.16.0
# — below it every cart write raised `undefined_column`),
# `Slug.put_slug/3` (2.4.0) and V171's projection pkeys (2.6.0).
# Patch-precise floor in the compound form, so the ceiling stays open
# through every later 2.x minor (a three-segment `~> 2.43.0` would pin
# one minor; see test/core_pin_conformance_test.exs).
pk_dep(:phoenix_kit, ">= 2.43.0 and < 3.0.0"),
# Gettext for per-module i18n of sidebar tab labels.
{:gettext, "~> 1.0"},
# Billing integration for checkout and order conversion.
# 0.13 is the floor (raised from 0.11 for per-domain-currency Э2,
# plan §0.3/§8.5): `PhoenixKitEcommerce.Events.subscribe_currencies/0`
# calls `PhoenixKitBilling.Events.subscribe_currencies/0` (subscribed
# from every storefront LiveView that shows a converted price —
# `catalog_category.ex`, `shop_catalog.ex`, `catalog_product.ex`,
# `checkout_page.ex`), which does not exist below the release
# carrying Э2's `{:currencies_changed, code}` PubSub event (CHANGELOG
# 0.13.0, merged upstream 2026-09-07 as PR #33). Below the floor this
# is `UndefinedFunctionError` on the storefront the moment a shopper
# opens the catalog, not at compile time — exactly what §8.5's hard
# floor exists to make impossible to install. 0.11's own reasons
# remain true too: `create_cart/1` and both add-to-cart paths call
# `PhoenixKitBilling.get_base_currency/0`, `get_display_currency/0`,
# `resolve_display_currency/1`, and `Currency.present/3`/
# `effective_rate/2`; 0.5.2's `payment_option_uuid` on
# `PhoenixKitBilling.Order` also still required — both now subsumed
# by the higher floor.
#
# 0.20 raised it again: checkout renders `BillingProfileFields` and
# builds its form over `BillingProfile.fields_changeset/3` /
# `form_fields/0`, none of which exist in 0.19.x - there the checkout
# page is an `UndefinedFunctionError` the moment a shopper opens it,
# not a compile error.
pk_dep(:phoenix_kit_billing, "~> 0.20"),
# Optional: only the AI-translate UI/adapter use it, and both compile out
# when it's absent (see ProductForm's @ai_translate? flag). Version tracks
# the actual API used (Translatable behaviour, AITranslate components).
#
# FLOOR RAISED for the translation-control design: the translation
# prompts this package rolls out (`AITranslatable`,
# `CategoryAITranslatable`) carry their entire source section as
# `{{SourceFields}}` — a variable bound only by phoenix_kit_ai
# 0.20.0, the release carrying the design's §9.1 change. An older
# engine renders `{{SourceFields}}` literally, so every translation
# call would go out with an empty SOURCE section: it costs money,
# fails to parse, and nothing but a log warning says why.
#
# 0.20.0 also carries §9.3 (`put_translation/4` receiving the
# source fields the worker actually read via `opts[:source_fields]`).
# Without it the staleness model of design §4.1 is inert rather than
# merely degraded: every write arrives with no source to hash, so
# nothing is ever fingerprinted, write-narrowing never engages, and
# each write erases the field's reference instead (see
# `TranslationFingerprint.apply_writes/3` — that erase is what keeps
# the sweep convergent against an engine lacking §9.3, at the cost
# of every touched pair falling back to `:unknown` for the operator
# to resolve by hand).
#
# RAISED AGAIN to 0.24: the sweep worker runs on the engine's shared
# `PhoenixKitAI.TranslationSweep` (its tick, gates, candidate
# selection and ceiling), first shipped in 0.24.0. The worker
# feature-detects the engine, so an older one degrades to "sweep
# unavailable" rather than crashing — but `~> 0.24` is what makes the
# sweep exist at all.
pk_dep(:phoenix_kit_ai, "~> 0.24", optional: true),
# Optional: only the "Stock tracking" switch uses it. Without it the
# switch is shown disabled with a pointer to Modules -> Not installed
# (see `PhoenixKitEcommerce.StockTracking`).
pk_dep(:phoenix_kit_warehouse, "~> 0.5", optional: true),
# No declared dependency on `phoenix_kit_catalogue`, though this app
# DOES call into `PhoenixKitCatalogue` directly (the ProductSource
# catalogue adapter, `Catalogue.Writer`, and the Shopify-sync 6a
# path call `Catalogue.{get_item_by_slug/3, get_category_by_slug/3,
# get_item!/1, create_item/2, update_item/2, list_catalogues/0,
# translated_*}`, `Catalogue.AttributeSets.{resolve_for_item/2,
# resolve_for_items/2, list_sets/1}`, `Catalogue.Slugs.from_title/2`
# and several `Schemas.*` structs — none of that is duck-typed).
# It IS true for the older extension-slot integration
# (`PhoenixKitEcommerce.Catalogue.Extension`), which is fully
# duck-typed and calls nothing in `PhoenixKitCatalogue` directly.
# No released version yet ships the API the ProductSource adapter
# targets, several of which exist only on the fork's own
# `feature/shop-extensions` branch — so a `~>` floor here could
# only be inaccurate, and every one of those call sites is
# unreachable unless a host resolves that exact branch. Add
# `pk_dep(:phoenix_kit_catalogue, "~> <floor>", optional: true)`
# once a release carries the API.
#
# `catalogue_test_deps/0` below is the narrow exception: a
# TEST-ONLY, path-only, opt-in bridge so the `:catalogue`-tagged
# tests in this repo (`writer_images_test.exs`,
# `writer_variants_test.exs`, `collection_sync_test.exs`,
# `shopify_sync_media_panel_test.exs`, `product_source/catalogue/*`)
# can actually execute against a real local checkout instead of
# staying excluded forever — set `PHOENIX_KIT_CATALOGUE_PATH` and
# `PHOENIX_KIT_ENTITIES_PATH` (matching branches the host app
# itself pins) and run `mix test`; leave them unset and no dependency
# is added by the bridge. The bridge itself never resolves via Hex,
# so the "no released version" rationale above is untouched. Note
# that the optional `phoenix_kit_warehouse` dependency above pulls
# `phoenix_kit_catalogue` (and `phoenix_kit_entities`) in from Hex
# transitively; with it resolved `test_helper.exs` no longer excludes
# `:catalogue` and those tests run by default.
# LiveView is needed for the admin and storefront pages.
{:phoenix_live_view, "~> 1.1"},
# Phoenix web framework (controllers, routing).
{:phoenix, "~> 1.7"},
# Ecto for database queries and schemas.
{:ecto_sql, "~> 3.12"},
# Background job processing (CSV imports, image migration).
{:oban, "~> 2.20"},
# UUIDv7 primary key generation.
{:uuidv7, "~> 1.0"},
# CSV parsing for product imports.
{:nimble_csv, "~> 1.2"},
# HTTP client for downloading product images.
{:req, "~> 0.5"},
# JSON encoding/decoding.
{:jason, "~> 1.4"},
# Documentation generation.
{:ex_doc, "~> 0.39", only: :dev, runtime: false},
# Code quality.
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
# `Phoenix.LiveViewTest` parses HTML via `lazy_html` for `element/2`,
# `render(view) =~ "..."`, etc. Test-only.
{:lazy_html, ">= 0.1.0", only: :test},
# `html_to_markdown_test.exs` renders the converter's Markdown output
# back through `MDEx.to_html!/1` to assert on CommonMark nesting
# semantics. Declared explicitly (rather than relying on the
# transitive resolution through `phoenix_kit`) so the test suite
# doesn't depend on another dependency's dependency tree. No
# `only: :test` here: `leaf` (a `phoenix_kit` dep) already
# requires `mdex` unrestricted to every environment, and Mix
# rejects a narrower `:only` than what's already resolved for the
# same package. Version matches the constraint `phoenix_kit`/`leaf`
# already lock to.
{:mdex, "~> 0.13"}
] ++ catalogue_test_deps()
end
# See the comment above `pk_dep(:phoenix_kit_catalogue, ...)`. Returns
# `[]` (no dependency declared at all) unless BOTH path env vars are
# set to a non-blank value; a path dep here can never fall back to
# resolving from Hex the way `pk_dep/3` does for the other optional
# deps, so an unset var can only ever mean "excluded", never "resolve
# some floor version that may not have the API".
defp catalogue_test_deps do
catalogue_path = System.get_env("PHOENIX_KIT_CATALOGUE_PATH")
entities_path = System.get_env("PHOENIX_KIT_ENTITIES_PATH")
if is_binary(catalogue_path) and catalogue_path != "" and is_binary(entities_path) and
entities_path != "" do
[
{:phoenix_kit_catalogue, path: catalogue_path, only: :test},
{:phoenix_kit_entities, path: entities_path, only: :test}
]
else
[]
end
end
defp package do
[
licenses: ["MIT"],
links: %{"GitHub" => @source_url},
# `priv/gettext`, not `priv`: Hex ignores .gitignore, and the test
# Storage writes real files into `priv/media`, which pushed a release
# tarball past Hex's 16 MB limit.
files: ~w(lib priv/gettext .formatter.exs mix.exs README.md CHANGELOG.md LICENSE)
]
end
defp docs do
[
main: "PhoenixKitEcommerce",
# Tags in this repo are bare version numbers, not v-prefixed — a "v" ref
# points at a tag that does not exist and 404s every HexDocs source link.
source_ref: @version,
source_url: @source_url
]
end
end