rails / execjs

Run JavaScript code from Ruby
MIT License
539 stars 283 forks source link

Allow package.json to have type: "module" #114

Open stoivo opened 2 years ago

stoivo commented 2 years ago

I changes my package.json(rails app), I added type: "module" and then assets:precompile stoped working.

root@runner--project-0-concurrent-0:/builds/project-0# rake assets:precompile
rake aborted!
ExecJS::RuntimeError: node:internal/errors:465
    ErrorCaptureStackTrace(err);
    ^

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension "" for /tmp/execjs20220609-2343-1czx37hjs. Loading extensionless files is not supported inside of "type":"module" package.json contexts. The package.json file /package.json caused this "type":"module" context. Try changing /tmp/execjs20220609-2343-1czx37hjs to have a file extension. Note the "bin" field of package.json can point to a file with an extension, for example {"type":"module","bin":{"execjs20220609-2343-1czx37hjs":"./tmp/execjs20220609-2343-1czx37hjs.js"}}
    at new NodeError (node:internal/errors:372:5)
    at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:76:11)
    at defaultGetFormat (node:internal/modules/esm/get_format:118:38)
    at defaultLoad (node:internal/modules/esm/load:21:20)
    at ESMLoader.load (node:internal/modules/esm/loader:407:26)
    at ESMLoader.moduleProvider (node:internal/modules/esm/loader:326:22)
    at new ModuleJob (node:internal/modules/esm/module_job:66:26)
    at ESMLoader.#createModuleJob (node:internal/modules/esm/loader:345:17)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:304:34)
    at async Promise.all (index 0) {
  code: 'ERR_UNKNOWN_FILE_EXTENSION'
}
(execjs):465
Tasks: TOP => assets:precompile
(See full trace by running task with --trace)

I believe node works like if you try to run a .js file it will use the type in package.json. If you want to run a script with commonjs name it .cjs or as a module .mjs.

back in 2014 it seam like it did add the extension to the file

commit b3f5dc69226c83ab5b3c47ba61c852831eeb2380
Author:     Joshua Peek <josh@joshpeek.com>
AuthorDate: Thu Jun 5 14:59:16 2014 -0500
Commit:     Joshua Peek <josh@joshpeek.com>
CommitDate: Thu Jun 5 14:59:16 2014 -0500

    Use similar code to Tempfile.create instead of Tempfile.open

...
-          tempfile = Tempfile.open(['execjs', '.js'])
+          tmpfile = create_tempfile(['execjs', 'js'])
...

I don't know why it was removed.

Tempfile.open has some finalizer and unlinking overhead that was don't actually use since we close the file right away. Tempfile.create was added in Ruby 2.1 to help, but it isn't widely available yet. Copy pasta some of its internals here.

https://github.com/sstephenson/execjs/pull/149

So I don't think it was on purpose. Also (I think) we didn't have js modules like we have them now so there where no reason to have the extension.

Maybe I should have created a minimal replication case but I didn't. It was just. Also I could not reproduce it on my mac but only on linux.

stoivo commented 2 years ago

After some more hours of research, I have learned some more useful information.

The cause of the error was: I had left a package.json in the root in my docker container with type: module. When execjs created a tmp file(/tmp/........./execjs1312312js on linux and /var/folders/kt/... on macs) there package.json the root file was detected as closes parent package.json. It contain type: module. When I remove it all works fine.

From the error message below, I think node is requires users to spesicy cjs or mjs when type module is set in package.json

Error [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension "" for /tmp/execjs20220616-1662-1q9n3vujs. Loading extensionless files is not supported inside of "type":"module" package.json contexts. The package.json file /package.json caused this "type":"module" context. Try changing /tmp/execjs20220616-1662-1q9n3vujs to have a file extension. Note the "bin" field of package.json can point to a file with an extension, for example {"type":"module","bin":{"execjs20220616-1662-1q9n3vujs":"./tmp/execjs20220616-1662-1q9n3vujs.js"}}

I still think this is a good thing too add since this is common.js

stoivo commented 2 years ago

If the nearest parent package.json lacks a "type" field, or contains "type": "commonjs", .js files are treated as CommonJS. If the volume root is reached and no package.json is found, .js files are treated as CommonJS. https://nodejs.org/api/packages.html#type