bhauman / figwheel-main

Figwheel Main provides tooling for developing ClojureScript applications
https://figwheel.org
Eclipse Public License 1.0
640 stars 93 forks source link

Error bundling with webpack on windows #342

Open ahwatts opened 11 months ago

ahwatts commented 11 months ago

When I try to bundle my code with Webpack on Windows, I'm getting the following error:

PS C:\Users\Andrew\Projects\dreamybandnames2> clj -M:cljs/deps -m figwheel.main --build-once dbn-frontend
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[Figwheel] Compiling build dbn-frontend to "target\js\dbn-frontend\main.js"
[Figwheel] Successfully compiled build dbn-frontend to "target\js\dbn-frontend\main.js" in 11.329 seconds.
[Figwheel] Bundling: npx webpack --mode=development --entry .\target\js\dbn-frontend\main.js --output-path .\target\js\dbn-frontend --output-filename main_bundle.js
[Figwheel:SEVERE] Bundling command failed: Cannot run program "npx": CreateProcess error=2, The system cannot find the file specified
Execution error (IOException) at java.lang.ProcessImpl/create (ProcessImpl.java:-2).
CreateProcess error=2, The system cannot find the file specified

Full report at:
C:\Users\Andrew\AppData\Local\Temp\clojure-5171192118823741169.edn

Something about how Figwheel is running npx is not working. It's present on my path:

PS C:\Users\Andrew\Projects\dreamybandnames2> npx --help
Run a command from a local or remote npm package

Usage:
npm exec -- <pkg>[@<version>] [args...]
npm exec --package=<pkg>[@<version>] -- <cmd> [args...]
npm exec -c '<cmd> [args...]'
npm exec --package=foo -c '<cmd> [args...]'

Options:
[--package <package-spec> [--package <package-spec> ...]] [-c|--call <call>]
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces] [--include-workspace-root]

alias: x

Run "npm help exec" for more info

And I can run that command from the PS command-line, though it fails for different, webpack-related reasons:

PS C:\Users\Andrew\Projects\dreamybandnames2> npx webpack --mode=development --entry .\target\js\dbn-frontend\main.js --output-path .\target\js\dbn-frontend --output-filename main_bundle.js
asset main_bundle.js 662 bytes [emitted] (name: main)

ERROR in main
Module not found: Error: Can't resolve '.\target\js\dbn-frontend\main.js' in 'C:\Users\Andrew\Projects\dreamybandnames2'
Did you mean './.\target\js\dbn-frontend\main.js'?
Requests that should resolve in the current directory need to start with './'.
Requests that start with a name are treated as module requests and resolve within module directories (node_modules).
If changing the source code is not an option there is also a resolve options called 'preferRelative' which tries to resolve these kind of requests in the current directory too.
resolve '.\target\js\dbn-frontend\main.js' in 'C:\Users\Andrew\Projects\dreamybandnames2'
  Parsed request is a module
  using description file: C:\Users\Andrew\Projects\dreamybandnames2\package.json (relative path: .)
    Field 'browser' doesn't contain a valid alias configuration
    resolve as module
      looking for modules in C:\Users\Andrew\Projects\dreamybandnames2\node_modules
        single file module
          using description file: C:\Users\Andrew\Projects\dreamybandnames2\package.json (relative path: ./node_modules/target/js/dbn-frontend/main.js)
            no extension
              Field 'browser' doesn't contain a valid alias configuration
              C:\Users\Andrew\Projects\dreamybandnames2\node_modules\target\js\dbn-frontend\main.js doesn't exist
            .js
              Field 'browser' doesn't contain a valid alias configuration
              C:\Users\Andrew\Projects\dreamybandnames2\node_modules\target\js\dbn-frontend\main.js.js doesn't exist
            .json
              Field 'browser' doesn't contain a valid alias configuration
              C:\Users\Andrew\Projects\dreamybandnames2\node_modules\target\js\dbn-frontend\main.js.json doesn't exist
            .wasm
              Field 'browser' doesn't contain a valid alias configuration
              C:\Users\Andrew\Projects\dreamybandnames2\node_modules\target\js\dbn-frontend\main.js.wasm doesn't exist
        C:\Users\Andrew\Projects\dreamybandnames2\node_modules\target\js\dbn-frontend\main.js doesn't exist
      C:\Users\Andrew\Projects\node_modules doesn't exist or is not a directory
      looking for modules in C:\Users\Andrew\node_modules
        single file module
          No description file found in C:\Users\Andrew\node_modules\target\js\dbn-frontend or above
          no extension
            Field 'browser' doesn't contain a valid alias configuration
            C:\Users\Andrew\node_modules\target\js\dbn-frontend\main.js doesn't exist
          .js
            Field 'browser' doesn't contain a valid alias configuration
            C:\Users\Andrew\node_modules\target\js\dbn-frontend\main.js.js doesn't exist
          .json
            Field 'browser' doesn't contain a valid alias configuration
            C:\Users\Andrew\node_modules\target\js\dbn-frontend\main.js.json doesn't exist
          .wasm
            Field 'browser' doesn't contain a valid alias configuration
            C:\Users\Andrew\node_modules\target\js\dbn-frontend\main.js.wasm doesn't exist
        C:\Users\Andrew\node_modules\target\js\dbn-frontend\main.js doesn't exist
      C:\Users\node_modules doesn't exist or is not a directory
      C:\node_modules doesn't exist or is not a directory

webpack 5.89.0 compiled with 1 error in 66 ms

My CLJS edn, dbn-frontend.cljs.edn:

{:main dbn.frontend.core
 :target :bundle
 :bundle-cmd {:none ["npx" "webpack"
                     "--mode=development"
                     "--entry" :output-to
                     "--output-path" :final-output-dir
                     "--output-filename" :final-output-filename]}
 :output-dir "target/js/dbn-frontend"
 :asset-path "js/dbn-frontend"}

Is there something special I need to do to get figwheel-main to work with NPM / Webpack in Windows? The config I have works correctly on Mac / Linux.

ahwatts commented 9 months ago

A partial solution to the first issue, via https://clojuredocs.org/clojure.java.shell/sh#example-60754e7fe4b0b1e3652d74c5

Apparently on Windows, clojure.java.shell/sh needs either the actual file name of the program to run (if it's not PowerShell or cmd) or else it needs to run the command in PowerShell or cmd, which handles omitting the extension.

So if I change dbn-frontend.cljs.edn to

{:main dbn.frontend.core
 :target :bundle
 :bundle-cmd {:none ["powershell" "npx" "webpack"
                     "--mode=development"
                     "--entry" :output-to
                     "--output-path" :final-output-dir
                     "--output-filename" :final-output-filename]}
 :output-dir "target/js/dbn-frontend"
 :asset-path "js/dbn-frontend"}

or

{:main dbn.frontend.core
 :target :bundle
 :bundle-cmd {:none ["npx.cmd" "webpack"
                     "--mode=development"
                     "--entry" :output-to
                     "--output-path" :final-output-dir
                     "--output-filename" :final-output-filename]}
 :output-dir "target/js/dbn-frontend"
 :asset-path "js/dbn-frontend"}

then the bundling command will at least run.

The second problem then appears to happen because figwheel uses clojure.java.io/file to assemble the paths to :output-to and :final-output-dir, which correctly uses \ as the directory separator on Windows, while Webpack appears to want the directory separator to be / there. If I run this command on Windows:

~\Projects\dreamybandnames2> npx webpack --mode=development --entry ./target/js/dbn-frontend/main.js --output-path ./target/js/dbn-frontend --output-filename main_bundle.js
asset main_bundle.js 2.38 MiB [compared for emit] (name: main)
runtime modules 2.37 KiB 7 modules
orphan modules 311 bytes [orphan] 2 modules
modules by path ./node_modules/ 1.82 MiB 311 modules
modules by path ./target/js/dbn-frontend/*.js 1.45 KiB
  ./target/js/dbn-frontend/main.js 1.19 KiB [built] [code generated]
  ./target/js/dbn-frontend/npm_deps.js 264 bytes [built] [code generated]
webpack 5.90.1 compiled successfully in 908 ms

that builds the bundle correctly. Interestingly, it seems like only the first separator needs to be /:

~\Projects\dreamybandnames2> npx webpack --mode=development --entry ./target\js\dbn-frontend\main.js --output-path ./target\js\dbn-frontend --output-filename main_bundle.js
asset main_bundle.js 2.38 MiB [compared for emit] (name: main)
runtime modules 2.37 KiB 7 modules
orphan modules 311 bytes [orphan] 2 modules
modules by path ./node_modules/ 1.82 MiB 311 modules
modules by path ./target/js/dbn-frontend/*.js 1.45 KiB
  ./target/js/dbn-frontend/main.js 1.19 KiB [built] [code generated]
  ./target/js/dbn-frontend/npm_deps.js 264 bytes [built] [code generated]
webpack 5.90.1 compiled successfully in 953 ms

That kind of makes it seem like that issue is really Webpack's, for requiring that relative paths start with ./ even on Windows.

This means that to solve this, two things need to happen:

  1. Somehow, I need to be able to have :bundle-cmd be different only on Windows.
  2. When generating the bundle CLI, File.separator needs to be /, or else the first .\ needs to be ./.

I'm not sure how to get either to happen while not breaking dev on other platforms. I could just modify dbn-frontend.cljs.edn and remember to not check in the change, or else I could have a different dbn-frontend.cljs.edn for doing dev on Windows, but I don't think I can do logic in an EDN file, right? As for File.separator, does Java or clojure.java.io even allow you to change it temporarily (or permanently)?