Repository: phoenixframework/phoenix
Pull request #4266 (2021-03-19 14:41:41)

Add suggestion about env vars


Patch:
diff --git a/guides/deployment/releases.md b/guides/deployment/releases.md
index d61805d58..dd43f0bd3 100644
--- a/guides/deployment/releases.md
+++ b/guides/deployment/releases.md
@@ -245,3 +245,4 @@ A few points about configuring a containerized application:
 
 - If you run your app in a container, the `Endpoint` needs to be configured to listen on a "public" `:ip` address (like `0.0.0.0.0.0.0.0`) so that the app can be reached from outside the container. Whether the host should publish the container's ports to its own public IP or to localhost depends on your needs.
 - The more configuration you can provide at runtime (using `config/runtime.exs`), the more reusable your images will be across environments. In particular, secrets like database credentials and API keys should not be compiled into the image, but rather should be provided when creating containers based on that image. This is why the `Endpoint`'s `:secret_key_base` is configured in `config/runtime.exs` by default.
+- If possible, any environment variables that are needed at runtime should be read in `config/runtime.exs`, not scattered throughout your code. Having them all visible in one place will make it easier to ensure the containers get what they need, especially if the person doing the infrastructure work does not work on the Elixir code. Libraries in particular should never directly read environment variables; all their configuration should be handed to them by the top-level application, preferably [without using the application environment](https://hexdocs.pm/elixir/library-guidelines.html#avoid-application-configuration).
diff --git a/guides/ecto.md b/guides/ecto.md
index 1d0628dc4..271224950 100644
--- a/guides/ecto.md
+++ b/guides/ecto.md
@@ -532,10 +532,7 @@ defmodule HelloPhoenix.MixProject do
       {:phoenix_ecto, "~> 4.0"},
       {:ecto_sql, "~> 3.5"},
       {:myxql, ">= 0.0.0"},
-      {:phoenix_html, "~> 2.11"},
-      {:phoenix_live_reload, "~> 1.2", only: :dev},
-      {:gettext, "~> 0.11"},
-      {:plug_cowboy, "~> 2.0"}
+      ...
     ]
   end
 end
diff --git a/guides/mix_tasks.md b/guides/mix_tasks.md
index 5052e7a21..b65f325ad 100644
--- a/guides/mix_tasks.md
+++ b/guides/mix_tasks.md
@@ -1,13 +1,13 @@
-# Mix Tasks
+# Mix tasks
 
-There are currently a number of built-in Phoenix-specific and Ecto-specific mix tasks available to us within a newly-generated application. We can also create our own application specific tasks.
+There are currently a number of built-in Phoenix-specific and Ecto-specific [Mix tasks](`Mix.Task`) available to us within a newly-generated application. We can also create our own application specific tasks.
 
 > Note to learn more about `mix`, you can read Elixir's official [Introduction to Mix](https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html).
 
 ## Phoenix tasks
 
 ```console
-➜ mix help --search "phx"
+$ mix help --search "phx"
 mix local.phx          # Updates the Phoenix project generator locally
 mix phx                # Prints Phoenix help information
 mix phx.digest         # Digests and compresses static files
@@ -35,7 +35,7 @@ We will cover all Phoenix Mix tasks, except `phx.new`, `phx.new.ecto`, and `phx.
 
 ### `mix phx.gen.html`
 
-Phoenix offers the ability to generate all the code to stand up a complete HTML resource - ecto migration, ecto context, controller with all the necessary actions, view, and templates. This can be a tremendous time saver. Let's take a look at how to make this happen.
+Phoenix offers the ability to generate all the code to stand up a complete HTML resource — Ecto migration, Ecto context, controller with all the necessary actions, view, and templates. This can be a tremendous time saver. Let's take a look at how to make this happen.
 
 The `mix phx.gen.html` task takes a number of arguments, the module name of the context, the module name of the schema, the resource name, and a list of column_name:type attributes. The module name we pass in must conform to the Elixir rules of module naming, following proper capitalization.
 
@@ -57,7 +57,7 @@ $ mix phx.gen.html Blog Post posts body:string word_count:integer
 * injecting test/hello/blog/blog_test.exs
 ```
 
-When `mix phx.gen.html` is done creating files, it helpfully tells us that we need to add a line to our router file as well as run our ecto migrations.
+When `mix phx.gen.html` is done creating files, it helpfully tells us that we need to add a line to our router file as well as run our Ecto migrations.
 
 ```console
 Add the resource to your browser scope in lib/hello_web/router.ex:
@@ -101,7 +101,7 @@ Add the resource to your browser scope in lib/hello_web/router.ex:
     resources "/posts", PostController
 ```
 
-Similarly - if we want a context created without a schema for our resource we can use the `--no-schema` flag.
+Similarly, if we want a context created without a schema for our resource we can use the `--no-schema` flag.
 
 ```console
 $ mix phx.gen.html Blog Post posts body:string word_count:integer --no-schema
@@ -123,7 +123,7 @@ It will tell us we need to add a line to our router file, but since we skipped t
 
 ### `mix phx.gen.json`
 
-Phoenix also offers the ability to generate all the code to stand up a complete JSON resource - ecto migration, ecto schema, controller with all the necessary actions and view. This command will not create any template for the app.
+Phoenix also offers the ability to generate all the code to stand up a complete JSON resource — Ecto migration, Ecto schema, controller with all the necessary actions and view. This command will not create any template for the app.
 
 The `mix phx.gen.json` task takes a number of arguments, the module name of the context, the module name of the schema, the resource name, and a list of column_name:type attributes. The module name we pass in must conform to the Elixir rules of module naming, following proper capitalization.
 
@@ -142,7 +142,7 @@ $ mix phx.gen.json Blog Post posts title:string content:string
 * injecting test/hello/blog/blog_test.exs
 ```
 
-When `mix phx.gen.json` is done creating files, it helpfully tells us that we need to add a line to our router file as well as run our ecto migrations.
+When `mix phx.gen.json` is done creating files, it helpfully tells us that we need to add a line to our router file as well as run our Ecto migrations.
 
 ```console
 Add the resource to your :api scope in lib/hello_web/router.ex:
@@ -168,7 +168,7 @@ warning: function HelloWeb.Router.Helpers.post_path/3 is undefined or private
 
 ### `mix phx.gen.context`
 
-If we don't need a complete HTML/JSON resource and instead are only interested in a context, we can use the `mix phx.gen.context` task. It will generate a context, a schema, a migration and a test case.
+If we don't need a complete HTML/JSON resource and only need a context, we can use the `mix phx.gen.context` task. It will generate a context, a schema, a migration and a test case.
 
 The `mix phx.gen.context` task takes a number of arguments, the module name of the context, the module name of the schema, the resource name, and a list of column_name:type attributes.
 
@@ -207,7 +207,7 @@ $ mix phx.gen.schema Accounts.Credential credentials email:string:unique user_id
 
 ### `mix phx.gen.auth`
 
-Phoenix also offers the ability to generate all of the code to stand up a complete authentication system - ecto migration, phoenix context, controllers, templates, etc. This can be a huge time saver, allowing you to quickly add authentication to your system and shift your focus back to the primary problems your application is trying to solve.
+Phoenix also offers the ability to generate all of the code to stand up a complete authentication system — Ecto migration, phoenix context, controllers, templates, etc. This can be a huge time saver, allowing you to quickly add authentication to your system and shift your focus back to the primary problems your application is trying to solve.
 
 The `mix phx.gen.auth` task takes the following arguments: the module name of the context, the module name of the schema, and a plural version of the schema name used to generate database tables and route helpers.
 
@@ -258,7 +258,7 @@ $ mix phx.gen.auth Accounts User users
 * injecting lib/hello_web/templates/layout/app.html.eex
 ```
 
-When `mix phx.gen.auth` is done creating files, it helpfully tells us that we need to re-fetch our dependencies as well as run our ecto migrations.
+When `mix phx.gen.auth` is done creating files, it helpfully tells us that we need to re-fetch our dependencies as well as run our Ecto migrations.
 
 ```console
 Please re-fetch your dependencies with the following command:
@@ -270,11 +270,11 @@ Remember to update your repository by running migrations:
   $ mix ecto.migrate
 ```
 
-A more complete walk-through of how to get started with this generator is available in the [`mix phx.gen.auth` Guide](mix_phx_gen_auth.html).
+A more complete walk-through of how to get started with this generator is available in the [`mix phx.gen.auth` authentication guide](mix_phx_gen_auth.html).
 
 ### `mix phx.gen.channel`
 
-This task will generate a basic Phoenix channel as well a test case for it. It takes the module name for the channel as argument:
+This task will generate a basic Phoenix channel as well a test case for it. It takes the module name for the channel as the only one argument:
 
 ```console
 $ mix phx.gen.channel Room
@@ -292,7 +292,7 @@ Add the channel to your `lib/hello_web/channels/user_socket.ex` handler, for exa
 
 ### `mix phx.gen.presence`
 
-This task will generate a Presence tracker. The module name can be passed as an argument,
+This task will generate a presence tracker. The module name can be passed as an argument,
 `Presence` is used if no module name is passed.
 
 ```console
@@ -302,7 +302,7 @@ $ lib/hello_web/channels/presence.ex
 
 ### `mix phx.routes`
 
-This task has a single purpose, to show us all the routes defined for a given router. We saw it used extensively in the [Routing Guide](routing.html).
+This task has a single purpose, to show us all the routes defined for a given router. We saw it used extensively in the [routing guide](routing.html).
 
 If we don't specify a router for this task, it will default to the router Phoenix generated for us.
 
@@ -325,13 +325,13 @@ This is the task we use to get our application running. It takes no arguments at
 $ mix phx.server
 [info] Running TaskTesterWeb.Endpoint with Cowboy on port 4000 (http)
 ```
-It silently ignores our `DoesNotExist` argument.
+It will silently ignore our `DoesNotExist` argument:
 
 ```console
 $ mix phx.server DoesNotExist
 [info] Running TaskTesterWeb.Endpoint with Cowboy on port 4000 (http)
 ```
-If we would like to start our application and also have an `iex` session open to it, we can run the mix task within `iex` like this, `iex -S mix phx.server`.
+If we would like to start our application and also have an `IEx` session open to it, we can run the Mix task within `iex` like this, `iex -S mix phx.server`.
 
 ```console
 $ iex -S mix phx.server
@@ -346,11 +346,11 @@ iex(1)>
 
 This task does two things, it creates a digest for our static assets and then compresses them.
 
-"Digest" here refers to an MD5 digest of the contents of an asset which gets added to the filename of that asset. This creates a sort of "fingerprint" for it. If the digest doesn't change, browsers and CDNs will use a cached version. If it does change, they will re-fetch the new version.
+"Digest" here refers to an MD5 digest of the contents of an asset which gets added to the filename of that asset. This creates a sort of fingerprint for it. If the digest doesn't change, browsers and CDNs will use a cached version. If it does change, they will re-fetch the new version.
 
 Before we run this task let's inspect the contents of two directories in our hello application.
 
-First `priv/static` which should look similar to this:
+First `priv/static/` which should look similar to this:
 
 ```console
 ├── images
@@ -376,7 +376,7 @@ $ mix phx.digest
 Check your digested files at 'priv/static'.
 ```
 
-We can now do as the task suggests and inspect the contents of `priv/static` directory. We'll see that all files from `assets/` have been copied over to `priv/static` and also each file now has a couple of versions. Those versions are:
+We can now do as the task suggests and inspect the contents of `priv/static/` directory. We'll see that all files from `assets/` have been copied over to `priv/static/` and also each file now has a couple of versions. Those versions are:
 
 * the original file
 * a compressed file with gzip
@@ -392,13 +392,13 @@ config :phoenix, :gzippable_exts, ~w(.js .css)
 > Note: We can specify a different output folder where `mix phx.digest` will put processed files. The first argument is the path where the static files are located.
 
 ```console
-$ mix phx.digest priv/static -o www/public
-Check your digested files at 'www/public'.
+$ mix phx.digest priv/static/ -o www/public/
+Check your digested files at 'www/public/'
 ```
 
 ## Ecto tasks
 
-Newly generated Phoenix applications now include ecto and postgrex as dependencies by default (which is to say, unless we use `mix phx.new` with the `--no-ecto` flag). With those dependencies come mix tasks to take care of common ecto operations. Let's see which tasks we get out of the box.
+Newly generated Phoenix applications now include Ecto and Postgrex as dependencies by default (which is to say, unless we use `mix phx.new` with the `--no-ecto` flag). With those dependencies come Mix tasks to take care of common Ecto operations. Let's see which tasks we get out of the box.
 
 ```console
 $ mix help --search "ecto"
@@ -418,7 +418,7 @@ Note: We can run any of the tasks above with the `--no-start` flag to execute th
 
 ### `mix ecto.create`
 
-This task will create the database specified in our repo. By default it will look for the repo named after our application (the one generated with our app unless we opted out of ecto), but we can pass in another repo if we want.
+This task will create the database specified in our repo. By default it will look for the repo named after our application (the one generated with our app unless we opted out of Ecto), but we can pass in another repo if we want.
 
 Here's what it looks like in action.
 
@@ -487,7 +487,7 @@ The database for OurCustom.Repo has been created.
 
 ### `mix ecto.drop`
 
-This task will drop the database specified in our repo. By default it will look for the repo named after our application (the one generated with our app unless we opted out of ecto). It will not prompt us to check if we're sure we want to drop the db, so do exercise caution.
+This task will drop the database specified in our repo. By default it will look for the repo named after our application (the one generated with our app unless we opted out of Ecto). It will not prompt us to check if we're sure we want to drop the database, so do exercise caution.
 
 ```console
 $ mix ecto.drop
@@ -553,7 +553,7 @@ children = [
 
 ### `mix ecto.gen.migration`
 
-Migrations are a programmatic, repeatable way to affect changes to a database schema. Migrations are also just modules, and we can create them with the `ecto.gen.migration` task. Let's walk through the steps to create a migration for a new comments table.
+Migrations are a programmatic, repeatable way to affect changes to a database schema. Migrations are also just modules, and we can create them with the [`ecto.gen.migration`](`mix ecto.gen.migration`) task. Let's walk through the steps to create a migration for a new comments table.
 
 We simply need to invoke the task with a `snake_case` version of the module name that we want. Preferably, the name will describe what we want the migration to do.
 
@@ -601,7 +601,7 @@ $ mix ecto.gen.migration -r OurCustom.Repo add_users
 ```
 
 For more information on how to modify your database schema please refer to the
-Ecto's migration DSL [ecto migration docs](https://hexdocs.pm/ecto_sql/Ecto.Migration.html).
+[Ecto's migration DSL docs](https://hexdocs.pm/ecto_sql/Ecto.Migration.html).
 For example, to alter an existing schema see the documentation on Ecto’s
 [`alter/2`](https://hexdocs.pm/ecto_sql/Ecto.Migration.html#alter/2) function.
 
@@ -631,7 +631,7 @@ version        |     inserted_at
 (2 rows)
 ```
 
-When we roll back a migration, `ecto.rollback` will remove the record representing this migration from `schema_migrations`.
+When we roll back a migration, [`ecto.rollback`](#mix-ecto-rollback) will remove the record representing this migration from `schema_migrations`.
 
 By default, `ecto.migrate` will execute all pending migrations. We can exercise more control over which migrations we run by specifying some options when we run the task.
 
@@ -661,7 +661,7 @@ mix ecto.migrate --to 20150317170448
 
 ### `mix ecto.rollback`
 
-The `ecto.rollback` task will reverse the last migration we have run, undoing the schema changes. `ecto.migrate` and `ecto.rollback` are mirror images of each other.
+The [`ecto.rollback`](`mix ecto.rollback`) task will reverse the last migration we have run, undoing the schema changes. [`ecto.migrate`](#mix-ecto-migrate) and `ecto.rollback` are mirror images of each other.
 
 ```console
 $ mix ecto.rollback
@@ -674,12 +674,12 @@ $ mix ecto.rollback
 
 ## Creating our own Mix task
 
-As we've seen throughout this guide, both mix itself and the dependencies we bring in to our application provide a number of really useful tasks for free. Since neither of these could possibly anticipate all our individual application's needs, mix allows us to create our own custom tasks. That's exactly what we are going to do now.
+As we've seen throughout this guide, both Mix itself and the dependencies we bring in to our application provide a number of really useful tasks for free. Since neither of these could possibly anticipate all our individual application's needs, Mix allows us to create our own custom tasks. That's exactly what we are going to do now.
 
-The first thing we need to do is create a `mix/tasks` directory inside of `lib`. This is where any of our application specific mix tasks will go.
+The first thing we need to do is create a `mix/tasks/` directory inside of `lib/`. This is where any of our application specific Mix tasks will go.
 
 ```console
-$ mkdir -p lib/mix/tasks
+$ mkdir -p lib/mix/tasks/
 ```
 
 Inside that directory, let's create a new file, `hello.greeting.ex`, that looks like this.
@@ -691,7 +691,7 @@ defmodule Mix.Tasks.Hello.Greeting do
   @shortdoc "Sends a greeting to us from Hello Phoenix"
 
   @moduledoc """
-  This is where we would put any long form documentation or doctests.
+  This is where we would put any long form documentation and doctests.
   """
 
   def run(_args) do
@@ -702,18 +702,18 @@ defmodule Mix.Tasks.Hello.Greeting do
 end
 ```
 
-Let's take a quick look at the moving parts involved in a working mix task.
+Let's take a quick look at the moving parts involved in a working Mix task.
 
-The first thing we need to do is name our module. All tasks must be defined in `Mix.Tasks` namespace. We'd like to invoke this as `mix hello.greeting`, so we complete the module name with
+The first thing we need to do is name our module. All tasks must be defined in the `Mix.Tasks` namespace. We'd like to invoke this as `mix hello.greeting`, so we complete the module name with
 `Hello.Greeting`.
 
-The `use Mix.Task` line brings in functionality from Mix that makes this module behave as a mix task.
+The `use Mix.Task` line brings in functionality from Mix that makes this module [behave as a Mix task](`Mix.Task`).
 
 The `@shortdoc` module attribute holds a string which will describe our task when users invoke `mix help`.
 
 `@moduledoc` serves the same function that it does in any module. It's where we can put long-form documentation and doctests, if we have any.
 
-The `run/1` function is the critical heart of any Mix task. It's the function that does all the work when users invoke our task. In ours, all we do is send a greeting from our app, but we can implement our `run/1` function to do whatever we need it to. Note that `Mix.shell().info/1` is the preferred way to print text back out to the user.
+The [`run/1`](`c:Mix.Task.run/1`) function is the critical heart of any Mix task. It's the function that does all the work when users invoke our task. In ours, all we do is send a greeting from our app, but we can implement our `run/1` function to do whatever we need it to. Note that [`Mix.shell().info/1`](`Mix.shell/0`) is the preferred way to print text back out to the user.
 
 Of course, our task is just a module, so we can define other private functions as needed to support our `run/1` function.
 
@@ -743,13 +743,13 @@ Greetings from the Hello Phoenix Application!
 
 Indeed it does.
 
-If you want to make your new mix task to use your application's infrastructure, you need to make sure the application is started when mix task is being executed. This is particularly useful if you need to access your database from within the mix task. Thankfully, mix makes it really easy for us:
+If you want to make your new Mix task to use your application's infrastructure, you need to make sure the application is started when Mix task is being executed. This is particularly useful if you need to access your database from within the Mix task. Thankfully, Mix makes it really easy for us:
 
 ```elixir
-  . . .
   def run(_args) do
     Mix.Task.run("app.start")
     Mix.shell().info("Now I have access to Repo and other goodies!")
+    Mix.shell().info("Greetings from the Hello Phoenix Application!")    
   end
-  . . .
 ```
+
diff --git a/guides/views.md b/guides/views.md
index a178790b4..b720964d7 100644
--- a/guides/views.md
+++ b/guides/views.md
@@ -268,7 +268,6 @@ end
 ```
 
 The name used in assigns is determined from the view. For example `PageView` will use `%{page: page}` and `AuthorView` will use `%{author: author}`. This can be overridden with the `as` option. Let's assume that the author view uses `%{writer: writer}` instead of `%{author: author}`:
->>>>>>> ff14fd75 (Fix grammar. Remove article "the" before controllers and views.)
 
 ```elixir
 def render("page_with_authors.json", %{page: page}) do
diff --git a/lib/mix/tasks/compile.phoenix.ex b/lib/mix/tasks/compile.phoenix.ex
index 010afcde4..a97a81c71 100644
--- a/lib/mix/tasks/compile.phoenix.ex
+++ b/lib/mix/tasks/compile.phoenix.ex
@@ -4,6 +4,11 @@ defmodule Mix.Tasks.Compile.Phoenix do
 
   @moduledoc """
   Compiles Phoenix source files that support code reloading.
+  
+  If you are using Elixir v1.11+ or later, there is no longer
+  a need to use this module as this functionality is now provided
+  by Elixir. Just remember to update `__phoenix_recompile__?` to
+  `__mix_recompile__?` in any module that may define it.
   """
 
   # TODO: Deprecate this module once we require Elixir v1.11+
diff --git a/lib/phoenix/code_reloader.ex b/lib/phoenix/code_reloader.ex
index b1b33211d..cfbb56264 100644
--- a/lib/phoenix/code_reloader.ex
+++ b/lib/phoenix/code_reloader.ex
@@ -2,18 +2,6 @@ defmodule Phoenix.CodeReloader do
   @moduledoc """
   A plug and module to handle automatic code reloading.
 
-  For each request, Phoenix goes through all modules and checks if any of
-  them implement a `__phoenix_recompile__?/0` function. If they do and
-  it returns true, the module source file is touched, forcing it to be
-  recompiled. For this functionality to work, Phoenix requires you to add
-  the `:phoenix` compiler to your list of compilers:
-
-      compilers: [:phoenix] ++ Mix.compilers()
-
-  This is useful, for example, to recompile modules that depend on external
-  systems, such as directories, databases, etc. Note if you simply depend on
-  external files, `@external_resource` annotation should be used.
-
   To avoid race conditions, all code reloads are funneled through a
   sequential call operation.
   """
diff --git a/mix.exs b/mix.exs
index d7ba09851..8755bb7f6 100644
--- a/mix.exs
+++ b/mix.exs
@@ -52,7 +52,7 @@ defmodule Phoenix.MixProject do
         stacktrace_depth: nil,
         filter_parameters: ["password"],
         serve_endpoints: false,
-        gzippable_exts: ~w(.js .css .txt .text .html .json .svg .eot .ttf),
+        gzippable_exts: ~w(.js .map .css .txt .text .html .json .svg .eot .ttf),
         static_compressors: [Phoenix.Digester.Gzip]
       ]
     ]
@@ -74,6 +74,7 @@ defmodule Phoenix.MixProject do
       # Docs dependencies (some for cross references)
       {:ex_doc, "~> 0.22", only: :docs},
       {:ecto, ">= 3.0.0", only: :docs},
+      {:ecto_sql, "~> 3.5", only: :docs},
       {:gettext, "~> 0.15.0", only: :docs},
       {:telemetry_poller, "~> 0.4", only: :docs},
       {:telemetry_metrics, "~> 0.4", only: :docs},
diff --git a/mix.lock b/mix.lock
index d333b9264..895029584 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,9 +1,12 @@
 %{
+  "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
   "cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "04fd8c6a39edc6aaa9c26123009200fc61f92a3a94f3178c527b70b767c6e605"},
   "cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm", "79f954a7021b302186a950a32869dbc185523d99d3e44ce430cd1f3289f41ed4"},
+  "db_connection": {:hex, :db_connection, "2.3.1", "4c9f3ed1ef37471cbdd2762d6655be11e38193904d9c5c1c9389f1b891a3088e", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "abaab61780dde30301d840417890bd9f74131041afd02174cf4e10635b3a63f5"},
   "decimal": {:hex, :decimal, "1.9.0", "83e8daf59631d632b171faabafb4a9f4242c514b0a06ba3df493951c08f64d07", [:mix], [], "hexpm", "b1f2343568eed6928f3e751cf2dffde95bfaa19dd95d09e8a9ea92ccfd6f7d85"},
   "earmark_parser": {:hex, :earmark_parser, "1.4.10", "6603d7a603b9c18d3d20db69921527f82ef09990885ed7525003c7fe7dc86c56", [:mix], [], "hexpm", "8e2d5370b732385db2c9b22215c3f59c84ac7dda7ed7e544d7c459496ae519c0"},
   "ecto": {:hex, :ecto, "3.5.5", "48219a991bb86daba6e38a1e64f8cea540cded58950ff38fbc8163e062281a07", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "98dd0e5e1de7f45beca6130d13116eae675db59adfa055fb79612406acf6f6f1"},
+  "ecto_sql": {:hex, :ecto_sql, "3.5.4", "a9e292c40bd79fff88885f95f1ecd7b2516e09aa99c7dd0201aa84c54d2358e4", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.5.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1fff1a28a898d7bbef263f1f3ea425b04ba9f33816d843238c84eff883347343"},
   "ex_doc": {:hex, :ex_doc, "0.23.0", "a069bc9b0bf8efe323ecde8c0d62afc13d308b1fa3d228b65bca5cf8703a529d", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "f5e2c4702468b2fd11b10d39416ddadd2fcdd173ba2a0285ebd92c39827a5a16"},
   "gettext": {:hex, :gettext, "0.15.0", "40a2b8ce33a80ced7727e36768499fc9286881c43ebafccae6bab731e2b2b8ce", [:mix], [], "hexpm", "d17ac3aa40835f7feb92913fcdf655edea59938fa8b25f5ce5d423e76a695c3b"},
   "jason": {:hex, :jason, "1.0.0", "0f7cfa9bdb23fed721ec05419bcee2b2c21a77e926bce0deda029b5adc716fe2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b96c400e04b7b765c0854c05a4966323e90c0d11fee0483b1567cda079abb205"},


Test patch:
diff --git a/test/phoenix/digester_test.exs b/test/phoenix/digester_test.exs
index 12cccaf8b..78594f2e6 100644
--- a/test/phoenix/digester_test.exs
+++ b/test/phoenix/digester_test.exs
@@ -24,6 +24,8 @@ defmodule Phoenix.DigesterTest do
       refute "phoenix.png.gz" in output_files
       assert "app.js" in output_files
       assert "app.js.gz" in output_files
+      assert "app.js.map" in output_files
+      assert "app.js.map.gz" in output_files
       assert "css/app.css" in output_files
       assert "css/app.css.gz" in output_files
       assert "manifest.json" in output_files