From fa07573511dedd227f99e7f05a6e9e6462aabe8a Mon Sep 17 00:00:00 2001 From: Zach Daniel Date: Thu, 15 Sep 2022 18:35:33 -0400 Subject: [PATCH] improvement: lots of docs fixes! --- .formatter.exs | 12 +- README.md | 9 +- assets/css/syntax.css | 4 + lib/ash_hq/docs/docs.ex | 5 +- .../changes/render_markdown.ex | 24 ++ .../extensions/render_markdown/highlighter.ex | 29 +- .../render_markdown/render_markdown.ex | 33 ++- .../transformers/add_search_structure.ex | 32 +++ lib/ash_hq/docs/flows/search/search.ex | 48 ++-- lib/ash_hq/docs/importer/importer.ex | 36 ++- lib/ash_hq/docs/resources/dsl/dsl.ex | 4 +- .../docs/resources/extension/extension.ex | 2 +- .../docs/resources/function/function.ex | 12 +- lib/ash_hq/docs/resources/guide/guide.ex | 16 +- .../library_version/library_version.ex | 2 +- lib/ash_hq/docs/resources/module/module.ex | 2 +- lib/ash_hq/docs/resources/option/option.ex | 6 +- lib/ash_hq_web/components/docs/functions.ex | 64 +++-- lib/ash_hq_web/components/right_nav.ex | 10 + lib/ash_hq_web/components/search.ex | 20 +- lib/ash_hq_web/pages/docs.ex | 39 ++- lib/ash_hq_web/pages/home.ex | 2 +- lib/ash_hq_web/router.ex | 10 + lib/ash_hq_web/schema.ex | 22 ++ mix.exs | 14 +- mix.lock | 14 +- .../20220825205417_migrate_resources23.exs | 24 +- ...1_install_pg_stat_statements_extension.exs | 6 +- ...220913230038_install_sslinfo_extension.exs | 6 +- .../20220914230709_migrate_resources25.exs | 79 ++++++ .../repo/dsls/20220914230709.json | 252 +++++++++++++++++ .../repo/extensions/20220914230709.json | 162 +++++++++++ .../repo/functions/20220914230709.json | 212 ++++++++++++++ .../repo/guides/20220914230709.json | 132 +++++++++ .../repo/library_versions/20220914230709.json | 102 +++++++ .../repo/modules/20220914230709.json | 142 ++++++++++ .../repo/options/20220914230709.json | 233 ++++++++++++++++ priv/scripts/build_dsl_docs.exs | 264 ++++++++++++++---- 38 files changed, 1902 insertions(+), 183 deletions(-) create mode 100644 lib/ash_hq_web/schema.ex create mode 100644 priv/repo/migrations/20220914230709_migrate_resources25.exs create mode 100644 priv/resource_snapshots/repo/dsls/20220914230709.json create mode 100644 priv/resource_snapshots/repo/extensions/20220914230709.json create mode 100644 priv/resource_snapshots/repo/functions/20220914230709.json create mode 100644 priv/resource_snapshots/repo/guides/20220914230709.json create mode 100644 priv/resource_snapshots/repo/library_versions/20220914230709.json create mode 100644 priv/resource_snapshots/repo/modules/20220914230709.json create mode 100644 priv/resource_snapshots/repo/options/20220914230709.json diff --git a/.formatter.exs b/.formatter.exs index 16da3e7..846ef62 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,9 +1,15 @@ [ - import_deps: [:ecto, :phoenix, :ash, :ash_postgres, :surface], - inputs: ["*.{ex,exs}", "priv/*/seeds.exs", "{config,lib,test}/**/*.{ex,exs}"], + import_deps: [:ecto, :phoenix, :ash, :ash_postgres, :ash_graphql, :surface], + inputs: [ + "*.{ex,exs}", + "priv/*/seeds.exs", + "priv/scripts/**/*.{ex,exs}", + "{config,lib,test}/**/*.{ex,exs}" + ], subdirectories: ["priv/*/migrations"], - plugins: [Spark.Formatter], + plugins: [Spark.Formatter, Surface.Formatter.Plugin], locals_without_parens: [ + auto_sanitize_name_attribute?: 1, name_attribute: 1, library_version_attribute: 1, load_for_search: 1, diff --git a/README.md b/README.md index 4239e35..9564749 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,11 @@ ## Getting Started 1. Fork and clone this repository. -2. Set up the project by running `mix do deps.get, deps.compile, setup` (the import may take a while). -3. Install the frontend assets by running `npm i --prefix assets`. -4. Run the server with `iex -S mix phx.server` -5. Open [http://localhost:4000](http://localhost:4000) +2. Set up the project by running `mix do deps.get, deps.compile, setup` +3. Run `mix import` to import the latest dependencies from hex (this may take a while) +4. Install the frontend assets by running `npm i --prefix assets`. +5. Run the server with `iex -S mix phx.server` +6. Open [http://localhost:4000](http://localhost:4000) ## A Bit of History diff --git a/assets/css/syntax.css b/assets/css/syntax.css index 52cf3ea..d696d47 100644 --- a/assets/css/syntax.css +++ b/assets/css/syntax.css @@ -373,6 +373,10 @@ background-color: #d2d2d2 } +.light .not-prose > .code-pre { + background-color: inherit +} + .light .inline { background-color: #d2d2d2; border-radius: 0.1rem; diff --git a/lib/ash_hq/docs/docs.ex b/lib/ash_hq/docs/docs.ex index 22dd8f9..3d3e5ba 100644 --- a/lib/ash_hq/docs/docs.ex +++ b/lib/ash_hq/docs/docs.ex @@ -2,7 +2,10 @@ defmodule AshHq.Docs do @moduledoc """ Handles documentation data. """ - use Ash.Api, otp_app: :ash_hq + use Ash.Api, + extensions: [ + AshGraphql.Api + ] execution do timeout 30_000 diff --git a/lib/ash_hq/docs/extensions/render_markdown/changes/render_markdown.ex b/lib/ash_hq/docs/extensions/render_markdown/changes/render_markdown.ex index 68aed94..0a8df94 100644 --- a/lib/ash_hq/docs/extensions/render_markdown/changes/render_markdown.ex +++ b/lib/ash_hq/docs/extensions/render_markdown/changes/render_markdown.ex @@ -12,6 +12,8 @@ defmodule AshHq.Docs.Extensions.RenderMarkdown.Changes.RenderMarkdown do source = Ash.Changeset.get_attribute(changeset, opts[:source]) text = remove_ash_hq_hidden_content(source) + attribute = Ash.Resource.Info.attribute(changeset.resource, opts[:destination]) + changeset = if text != source do Ash.Changeset.force_change_attribute(changeset, opts[:source], text) @@ -38,11 +40,29 @@ defmodule AshHq.Docs.Extensions.RenderMarkdown.Changes.RenderMarkdown do html_doc = AshHq.Docs.Extensions.RenderMarkdown.Highlighter.highlight(html_doc) + html_doc = + case attribute.type do + {:array, _} -> + List.wrap(html_doc) + + _ -> + html_doc + end + Ash.Changeset.force_change_attribute(changeset, opts[:destination], html_doc) {:ok, html_doc, _} -> html_doc = AshHq.Docs.Extensions.RenderMarkdown.Highlighter.highlight(html_doc) + html_doc = + case attribute.type do + {:array, _} -> + List.wrap(html_doc) + + _ -> + html_doc + end + Ash.Changeset.force_change_attribute(changeset, opts[:destination], html_doc) end else @@ -53,6 +73,10 @@ defmodule AshHq.Docs.Extensions.RenderMarkdown.Changes.RenderMarkdown do defp remove_ash_hq_hidden_content(nil), do: nil + defp remove_ash_hq_hidden_content(strings) when is_list(strings) do + Enum.map(strings, &remove_ash_hq_hidden_content/1) + end + defp remove_ash_hq_hidden_content(string) do string |> String.split(~r/\<\!---.*ash-hq-hide-start.*--\>/) diff --git a/lib/ash_hq/docs/extensions/render_markdown/highlighter.ex b/lib/ash_hq/docs/extensions/render_markdown/highlighter.ex index 260529a..b091205 100644 --- a/lib/ash_hq/docs/extensions/render_markdown/highlighter.ex +++ b/lib/ash_hq/docs/extensions/render_markdown/highlighter.ex @@ -6,6 +6,10 @@ defmodule AshHq.Docs.Extensions.RenderMarkdown.Highlighter do @doc """ Highlights all code block in an already generated HTML document. """ + def highlight(html) when is_list(html) do + Enum.map(html, &highlight/1) + end + def highlight(html) do Regex.replace( ~r/
([^<]*)<\/code><\/pre>/,
@@ -16,7 +20,6 @@ defmodule AshHq.Docs.Extensions.RenderMarkdown.Highlighter do
 
   defp highlight_code_block(full_block, lang, code) do
     case pick_language_and_lexer(lang) do
-      {_language, nil, _opts} -> full_block
       {language, lexer, opts} -> render_code(language, lexer, opts, code)
     end
   end
@@ -31,17 +34,21 @@ defmodule AshHq.Docs.Extensions.RenderMarkdown.Highlighter do
   end
 
   defp render_code(lang, lexer, lexer_opts, code) do
-    highlighted =
-      code
-      |> unescape_html()
-      |> IO.iodata_to_binary()
-      |> Makeup.highlight_inner_html(
-        lexer: lexer,
-        lexer_options: lexer_opts,
-        formatter_options: [highlight_tag: "span"]
-      )
+    if lexer do
+      highlighted =
+        code
+        |> unescape_html()
+        |> IO.iodata_to_binary()
+        |> Makeup.highlight_inner_html(
+          lexer: lexer,
+          lexer_options: lexer_opts,
+          formatter_options: [highlight_tag: "span"]
+        )
 
-    ~s(
#{highlighted}
) + ~s(
#{highlighted}
) + else + ~s(
#{code}
) + end end entities = [{"&", ?&}, {"<", ?<}, {">", ?>}, {""", ?"}, {"'", ?'}] diff --git a/lib/ash_hq/docs/extensions/render_markdown/render_markdown.ex b/lib/ash_hq/docs/extensions/render_markdown/render_markdown.ex index c779e54..91184a2 100644 --- a/lib/ash_hq/docs/extensions/render_markdown/render_markdown.ex +++ b/lib/ash_hq/docs/extensions/render_markdown/render_markdown.ex @@ -38,7 +38,15 @@ defmodule AshHq.Docs.Extensions.RenderMarkdown do Map.get(record, render_attributes(resource)[key]) on_demand? -> - as_html!(Map.get(record, key) || "", header_ids?(resource)) + case Map.get(record, key) do + value when is_list(value) -> + Enum.map(value, fn value -> + as_html!(value || "", header_ids?(resource)) + end) + + value -> + as_html!(value || "", header_ids?(resource)) + end true -> raise "#{resource} dos not render #{key} as markdown. Pass the `on_demand?` argument as `true` to render it dynamically." @@ -55,12 +63,35 @@ defmodule AshHq.Docs.Extensions.RenderMarkdown do "" end + def as_html!(text, add_ids?) when is_list(text) do + Enum.map(text, &as_html!(&1, add_ids?)) + end + def as_html!(text, add_ids?) do text |> Earmark.as_html!(opts(add_ids?)) |> AshHq.Docs.Extensions.RenderMarkdown.Highlighter.highlight() end + def as_html(text, add_ids?) when is_list(text) do + Enum.reduce_while(text, {:ok, [], []}, fn text, {:ok, list, errors} -> + case as_html(text, add_ids?) do + {:ok, text, new_errors} -> + {:cont, {:ok, [text | list], errors ++ new_errors}} + + other -> + {:halt, other} + end + end) + |> case do + {:ok, list, errors} -> + {:ok, Enum.reverse(list), errors} + + other -> + other + end + end + def as_html(text, add_ids?) do text |> Earmark.as_html(opts(add_ids?)) diff --git a/lib/ash_hq/docs/extensions/search/transformers/add_search_structure.ex b/lib/ash_hq/docs/extensions/search/transformers/add_search_structure.ex index aba4ba5..1b4f634 100644 --- a/lib/ash_hq/docs/extensions/search/transformers/add_search_structure.ex +++ b/lib/ash_hq/docs/extensions/search/transformers/add_search_structure.ex @@ -48,9 +48,41 @@ defmodule AshHq.Docs.Extensions.Search.Transformers.AddSearchStructure do |> add_matches_calculation(config) |> add_indexes(config) |> add_html_for_calculation(config) + |> add_additional_html_calculations(config) |> add_match_rank_calculation(config)} end + defp add_additional_html_calculations(dsl_state, config) do + dsl_state + |> Transformer.get_option([:render_markdown], :render_attributes) + |> List.wrap() + |> Enum.reject(fn {source, _} -> + config.doc_attribute == source + end) + |> Enum.reduce(dsl_state, fn {_, dest}, dsl_state -> + name = :"#{dest}_for" + type = Ash.Resource.Info.attribute(dsl_state, dest).type + + dsl_state + |> Transformer.add_entity( + [:calculations], + Transformer.build_entity!(Ash.Resource.Dsl, [:calculations], :calculate, + name: name, + type: type, + arguments: [html_for_argument()], + calculation: + Ash.Query.expr( + if ^ref(config.show_docs_on) == ^arg(:for) do + ^ref(dest) + else + nil + end + ) + ) + ) + end) + end + defp add_html_for_calculation(dsl_state, config) do if config.doc_attribute do html_for_attribute = diff --git a/lib/ash_hq/docs/flows/search/search.ex b/lib/ash_hq/docs/flows/search/search.ex index 861a4bb..e0d3625 100644 --- a/lib/ash_hq/docs/flows/search/search.ex +++ b/lib/ash_hq/docs/flows/search/search.ex @@ -4,92 +4,92 @@ defmodule AshHq.Docs.Search do use Ash.Flow, otp_app: :ash_hq flow do - api AshHq.Docs + api(AshHq.Docs) - description """ + description(""" Runs a search over all searchable items. - """ + """) argument :query, :string do - allow_nil? false - constraints trim?: false, allow_empty?: true + allow_nil?(false) + constraints(trim?: false, allow_empty?: true) end argument :library_versions, {:array, :uuid} do - allow_nil? false + allow_nil?(false) end - argument :types, {:array, :string} + argument(:types, {:array, :string}) - returns :build_results + returns(:build_results) end steps do custom :options, AshHq.Docs.Search.Steps.SearchResource do - input %{ + input(%{ query: arg(:query), library_versions: arg(:library_versions), types: arg(:types), resource: AshHq.Docs.Option - } + }) end custom :dsls, AshHq.Docs.Search.Steps.SearchResource do - input %{ + input(%{ query: arg(:query), library_versions: arg(:library_versions), types: arg(:types), resource: AshHq.Docs.Dsl - } + }) end custom :guides, AshHq.Docs.Search.Steps.SearchResource do - input %{ + input(%{ query: arg(:query), library_versions: arg(:library_versions), types: arg(:types), resource: AshHq.Docs.Guide - } + }) end custom :library_versions, AshHq.Docs.Search.Steps.SearchResource do - input %{ + input(%{ query: arg(:query), library_versions: arg(:library_versions), types: arg(:types), resource: AshHq.Docs.LibraryVersion - } + }) end custom :extensions, AshHq.Docs.Search.Steps.SearchResource do - input %{ + input(%{ query: arg(:query), library_versions: arg(:library_versions), types: arg(:types), resource: AshHq.Docs.Extension - } + }) end custom :functions, AshHq.Docs.Search.Steps.SearchResource do - input %{ + input(%{ query: arg(:query), library_versions: arg(:library_versions), types: arg(:types), resource: AshHq.Docs.Function - } + }) end custom :modules, AshHq.Docs.Search.Steps.SearchResource do - input %{ + input(%{ query: arg(:query), library_versions: arg(:library_versions), types: arg(:types), resource: AshHq.Docs.Module - } + }) end custom :build_results, AshHq.Docs.Search.Steps.BuildResults do - input %{ + input(%{ dsls: result(:dsls), options: result(:options), guides: result(:guides), @@ -97,7 +97,7 @@ defmodule AshHq.Docs.Search do extensions: result(:extensions), functions: result(:functions), modules: result(:modules) - } + }) end end end diff --git a/lib/ash_hq/docs/importer/importer.ex b/lib/ash_hq/docs/importer/importer.ex index 410f49d..8ae82eb 100644 --- a/lib/ash_hq/docs/importer/importer.ex +++ b/lib/ash_hq/docs/importer/importer.ex @@ -53,16 +53,40 @@ defmodule AshHq.Docs.Importer do |> Enum.each(fn version -> file = Path.expand("./#{Ash.UUID.generate()}.json") + # Just throwing in a bunch of things here to see if it fixes the issue + # don't actually think they matter, but might as well try + env_to_unset = [ + "RELEASE_BOOT_SCRIPT", + "RELEASE_MODE", + "RELEASE_COMMAND", + "BINDIR", + "RELEASE_REMOTE_VM_ARGS", + "RELEASE_ROOT", + "ROOTDIR", + "RELEASE_NODE", + "RELEASE_VSN", + "RELEASE_PROG", + "RELEASE_TMP", + "RELEASE_SYS_CONFIG", + "RELEASE_NAME", + "RELEASE_RELEASE_VM_ARGS", + "RELEASE_COOKIE" + ] + result = try do with_retry(fn -> {_, 0} = - System.cmd("elixir", [ - Path.join([:code.priv_dir(:ash_hq), "scripts", "build_dsl_docs.exs"]), - name, - version, - file - ]) + System.cmd( + "elixir", + [ + Path.join([:code.priv_dir(:ash_hq), "scripts", "build_dsl_docs.exs"]), + name, + version, + file + ], + env: Map.new(env_to_unset, &{&1, nil}) + ) output = File.read!(file) :erlang.binary_to_term(Base.decode64!(String.trim(output))) diff --git a/lib/ash_hq/docs/resources/dsl/dsl.ex b/lib/ash_hq/docs/resources/dsl/dsl.ex index add3db3..2d9d84f 100644 --- a/lib/ash_hq/docs/resources/dsl/dsl.ex +++ b/lib/ash_hq/docs/resources/dsl/dsl.ex @@ -112,11 +112,11 @@ defmodule AshHq.Docs.Dsl do relationships do belongs_to :library_version, AshHq.Docs.LibraryVersion do - required? true + allow_nil? true end belongs_to :extension, AshHq.Docs.Extension do - required? true + allow_nil? true end belongs_to :dsl, __MODULE__ diff --git a/lib/ash_hq/docs/resources/extension/extension.ex b/lib/ash_hq/docs/resources/extension/extension.ex index 818c680..0babd3f 100644 --- a/lib/ash_hq/docs/resources/extension/extension.ex +++ b/lib/ash_hq/docs/resources/extension/extension.ex @@ -93,7 +93,7 @@ defmodule AshHq.Docs.Extension do relationships do belongs_to :library_version, AshHq.Docs.LibraryVersion do - required? true + allow_nil? true end has_many :dsls, AshHq.Docs.Dsl diff --git a/lib/ash_hq/docs/resources/function/function.ex b/lib/ash_hq/docs/resources/function/function.ex index 0a27b82..bcc3977 100644 --- a/lib/ash_hq/docs/resources/function/function.ex +++ b/lib/ash_hq/docs/resources/function/function.ex @@ -10,7 +10,7 @@ defmodule AshHq.Docs.Function do end render_markdown do - render_attributes doc: :doc_html + render_attributes doc: :doc_html, heads: :heads_html header_ids? false end @@ -53,7 +53,7 @@ defmodule AshHq.Docs.Function do end attribute :type, :atom do - constraints one_of: [:function, :macro, :callback] + constraints one_of: [:function, :macro, :callback, :type] allow_nil? false end @@ -61,6 +61,10 @@ defmodule AshHq.Docs.Function do default [] end + attribute :heads_html, {:array, :string} do + default [] + end + attribute :doc, :string do allow_nil? false constraints trim?: false, allow_empty?: true @@ -102,11 +106,11 @@ defmodule AshHq.Docs.Function do relationships do belongs_to :library_version, AshHq.Docs.LibraryVersion do - required? true + allow_nil? true end belongs_to :module, AshHq.Docs.Module do - required? true + allow_nil? true end end end diff --git a/lib/ash_hq/docs/resources/guide/guide.ex b/lib/ash_hq/docs/resources/guide/guide.ex index cdf9a03..242a5d0 100644 --- a/lib/ash_hq/docs/resources/guide/guide.ex +++ b/lib/ash_hq/docs/resources/guide/guide.ex @@ -2,7 +2,10 @@ defmodule AshHq.Docs.Guide do @moduledoc false use AshHq.Resource, data_layer: AshPostgres.DataLayer, - extensions: [AshHq.Docs.Extensions.Search, AshHq.Docs.Extensions.RenderMarkdown] + extensions: [ + AshHq.Docs.Extensions.Search, + AshHq.Docs.Extensions.RenderMarkdown + ] resource do description "Represents a markdown guide exposed by a library" @@ -18,7 +21,7 @@ defmodule AshHq.Docs.Guide do load_for_search library_version: [:library_name, :library_display_name] show_docs_on :route sanitized_name_attribute :route - auto_sanitize_name_attribute?(false) + auto_sanitize_name_attribute? false end code_interface do @@ -27,6 +30,13 @@ defmodule AshHq.Docs.Guide do actions do defaults [:create, :read, :update, :destroy] + + read :paginate do + pagination do + countable true + keyset? true + end + end end changes do @@ -76,7 +86,7 @@ defmodule AshHq.Docs.Guide do relationships do belongs_to :library_version, AshHq.Docs.LibraryVersion do - required? true + allow_nil? true end end end diff --git a/lib/ash_hq/docs/resources/library_version/library_version.ex b/lib/ash_hq/docs/resources/library_version/library_version.ex index 5b4de00..00702af 100644 --- a/lib/ash_hq/docs/resources/library_version/library_version.ex +++ b/lib/ash_hq/docs/resources/library_version/library_version.ex @@ -120,7 +120,7 @@ defmodule AshHq.Docs.LibraryVersion do relationships do belongs_to :library, AshHq.Docs.Library do - required? true + allow_nil? true end has_many :extensions, AshHq.Docs.Extension diff --git a/lib/ash_hq/docs/resources/module/module.ex b/lib/ash_hq/docs/resources/module/module.ex index 718496d..46a20c1 100644 --- a/lib/ash_hq/docs/resources/module/module.ex +++ b/lib/ash_hq/docs/resources/module/module.ex @@ -90,7 +90,7 @@ defmodule AshHq.Docs.Module do relationships do belongs_to :library_version, AshHq.Docs.LibraryVersion do - required? true + allow_nil? true end has_many :functions, AshHq.Docs.Function diff --git a/lib/ash_hq/docs/resources/option/option.ex b/lib/ash_hq/docs/resources/option/option.ex index ac35702..b480de0 100644 --- a/lib/ash_hq/docs/resources/option/option.ex +++ b/lib/ash_hq/docs/resources/option/option.ex @@ -111,15 +111,15 @@ defmodule AshHq.Docs.Option do relationships do belongs_to :dsl, AshHq.Docs.Dsl do - required? true + allow_nil? true end belongs_to :library_version, AshHq.Docs.LibraryVersion do - required? true + allow_nil? true end belongs_to :extension, AshHq.Docs.Extension do - required? true + allow_nil? true end end end diff --git a/lib/ash_hq_web/components/docs/functions.ex b/lib/ash_hq_web/components/docs/functions.ex index 1e1de55..130adaf 100644 --- a/lib/ash_hq_web/components/docs/functions.ex +++ b/lib/ash_hq_web/components/docs/functions.ex @@ -17,30 +17,58 @@ defmodule AshHqWeb.Components.Docs.Functions do {#case Enum.filter(@functions, &(&1.type == @type))} {#match []} {#match functions} -

{@header}

+

{@header}

{#for function <- functions} -