Flame_Phoenix
Flame_Phoenix

Reputation: 17574

Cannot load assets in Phoenix umbrella app

Background

I have an old umbrella app that I am trying to bring to life once again. To do this I decided to add a phoenix project under the umbrella using:

mix phx.new.web hello_test --no-ecto

Notice the --no-ecto part, this is important.

Setup

After running said command inside my umbrella apps folder I changed my umbrella's config/config.exs file by adding the following:

config :phoenix, json_library: Jason

With this out of the way, I went to apps/hello_test/lib/hello_test/application.ex and added to the start function the following:

children = [
      # Start the Telemetry supervisor
      HelloTest.Telemetry,
      # Start the Endpoint (http/https)
      HelloTest.Endpoint,
      # Start a worker by calling: HelloTest.Worker.start_link(arg)
      # {HelloTest.Worker, arg}
      {Phoenix.PubSub, name: HelloTest.PubSub}
    ]

Then I ran mix assets.deploy inside of the hello_test apps folder followed by mix phx.server.

Problem

Now at this point I was expecting to see some nice welcome page when loading to localhost:4000. Instead I get a page with no css and an error message:

16:17:44.695 [debug] Processing with HelloTest.PageController.index/2
  Parameters: %{}
  Pipelines: [:browser]

16:17:44.737 [info]  Sent 200 in 51ms

16:17:44.821 [info]  GET /assets/app.js

16:17:44.887 [info]  GET /assets/app.css

16:17:44.887 [debug] ** (Phoenix.Router.NoRouteError) no route found for GET /assets/app.js (HelloTest.Router)
    (hello_test 0.1.0) lib/phoenix/router.ex:406: HelloTest.Router.call/2
    (hello_test 0.1.0) lib/hello_test/endpoint.ex:1: HelloTest.Endpoint.plug_builder_call/2
    (hello_test 0.1.0) lib/plug/debugger.ex:136: HelloTest.Endpoint."call (overridable 3)"/2
    (hello_test 0.1.0) lib/hello_test/endpoint.ex:1: HelloTest.Endpoint.call/2
    (phoenix 1.6.5) lib/phoenix/endpoint/cowboy2_handler.ex:54: Phoenix.Endpoint.Cowboy2Handler.init/4
    (cowboy 2.9.0) c:/Users/User/Workplace/market_manager/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
    (cowboy 2.9.0) c:/Users/User/Workplace/market_manager/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
    (cowboy 2.9.0) c:/Users/User/Workplace/market_manager/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3


16:17:44.897 [debug] ** (Phoenix.Router.NoRouteError) no route found for GET /assets/app.css (HelloTest.Router)
    (hello_test 0.1.0) lib/phoenix/router.ex:406: HelloTest.Router.call/2
    (hello_test 0.1.0) lib/hello_test/endpoint.ex:1: HelloTest.Endpoint.plug_builder_call/2
    (hello_test 0.1.0) lib/plug/debugger.ex:136: HelloTest.Endpoint."call (overridable 3)"/2
    (hello_test 0.1.0) lib/hello_test/endpoint.ex:1: HelloTest.Endpoint.call/2
    (phoenix 1.6.5) lib/phoenix/endpoint/cowboy2_handler.ex:54: Phoenix.Endpoint.Cowboy2Handler.init/4
    (cowboy 2.9.0) c:/Users/User/Workplace/market_manager/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2
    (cowboy 2.9.0) c:/Users/User/Workplace/market_manager/deps/cowboy/src/cowboy_stream_h.erl:306: :cowboy_stream_h.execute/3
    (cowboy 2.9.0) c:/Users/User/Workplace/market_manager/deps/cowboy/src/cowboy_stream_h.erl:295: :cowboy_stream_h.request_process/3

To be fair the setup done until now was not trivial, but I managed it by searching endlessly in old github issues and in this community.

However, now its different. This is as simple as any phoenix umbrella app can be and yet it still does not work out of the box.

The only solutions I found were for very old Phoenix versions, ones that still use NPM. I am using the latest version of phoenix, so those solutions do not apply:

λ mix phx.new --version
Phoenix installer v1.6.5

λ elixir -v
Erlang/OTP 24 [erts-12.1.4] [source] [64-bit] [smp:6:6] [ds:6:6:10] [async-threads:1] [jit]

Elixir 1.13.1 (compiled with Erlang/OTP 22)

How can I get rid of this error and make my app run correctly?

Upvotes: 2

Views: 845

Answers (2)

Dan Thiffault
Dan Thiffault

Reputation: 51

I just ran into this myself. By default under config/dev.exs the Endpoint configuration has an esbuild key that points to the default esbuild profile:

config :admin, AdminWeb.Endpoint,
  http: [ip: {127, 0, 0, 12}, port: 4000],
  url: [host: "admin.myapp.test"],
  check_origin: false,
  code_reloader: true,
  debug_errors: true,
  secret_key_base: "secret",
  watchers: [
    # Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
    esbuild: {Esbuild, :install_and_run, [:default, ~w(--sourcemap=inline --watch)]}
  ]

That profile is defined under config/config.exs like this:

config :esbuild,
  version: "0.14.0",
  default: [
    args:
      ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),
    cd: Path.expand("../apps/myapp_web/assets", __DIR__),
    env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
  ]

which points to the assets path in the default web application. In order to support multiple web apps you need to add a second config to config/config.exs

config :esbuild,
  version: "0.14.0",
  default: [
    args:
      ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),
    cd: Path.expand("../apps/myapp_web/assets", __DIR__),
    env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
  ],
admin: [
    args:
      ~w(js/app.js --bundle --target=es2017 --outdir=../priv/static/assets --external:/fonts/* --external:/images/*),
    cd: Path.expand("../apps/admin_web/assets", __DIR__),
    env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
  ],

changing both the key, admin in this case, and the corresponding path under your /apps/ directory. Then back in config/dev.exs switch your endpoint config to point to the new esbuild profile you just created.

config :admin, AdminWeb.Endpoint,
  http: [ip: {127, 0, 0, 12}, port: 4000],
  url: [host: "admin.myapp.test"],
  check_origin: false,
  code_reloader: true,
  debug_errors: true,
  secret_key_base: "secret",
  watchers: [
    # Start the esbuild watcher by calling Esbuild.install_and_run(:default, args)
    esbuild: {Esbuild, :install_and_run, [:admin, ~w(--sourcemap=inline --watch)]}
  ]

Depending on your release configuration, you may need to do this for your other environments as well, but if you're building assets as part of your release you should be able to get away without doing that.

Upvotes: 5

Flame_Phoenix
Flame_Phoenix

Reputation: 17574

Answer

So after some research I ended up creating a fresh new umbrella project with a child phoenix app inside. The issue was nowhere to be seen and everything worked properly.

mix new koko --umbrella
cd koko/apps
mix phx.new.web hello --no-ecto
cd hello
mix assets.deploy

So everything points to some issue in my old umbrella app. Because I wanted to do more tests, I created another phoenix app alongside the hello one.

Then I was able to replicate the issue.

At this point, I suspect that umbrella apps will have issues if you try to:

  • Add multiple phoenix child apps
  • Add a new phoenix child app when the project already had one in the past (and got deleted)

Why this is the case is beyond me, but what is not beyond me is the implication here: I have to remake my project with a fresh umbrella app and start over.

Upvotes: 3

Related Questions