Open baltpeter opened 1 week ago
Here's the code that causes this problem. We're trying to find appstraction
's version by importing its package.json
:
I think that problem is that cli
is using tsc
instead of a "proper" bundler like Parcel.
And Node does not support importing JSON files without an import assertion (require()
works fine, though).
I tried this example:
import abc from './abc.json';
console.log(abc);
And indeed:
❯ node index.mjs
node:internal/errors:496
ErrorCaptureStackTrace(err);
^
TypeError [ERR_IMPORT_ASSERTION_TYPE_MISSING]: Module "file:///tmp/sdfsdfsd/abc.json" needs an import assertion of type "json"
at new NodeError (node:internal/errors:405:5)
at validateAssertions (node:internal/modules/esm/assert:94:15)
at defaultLoad (node:internal/modules/esm/load:93:3)
at DefaultModuleLoader.load (node:internal/modules/esm/loader:263:26)
at DefaultModuleLoader.moduleProvider (node:internal/modules/esm/loader:179:22)
at new ModuleJob (node:internal/modules/esm/module_job:63:26)
at #createModuleJob (node:internal/modules/esm/loader:203:17)
at DefaultModuleLoader.getJobFromResolveResult (node:internal/modules/esm/loader:156:34)
at DefaultModuleLoader.getModuleJob (node:internal/modules/esm/loader:141:17)
at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:76:33) {
code: 'ERR_IMPORT_ASSERTION_TYPE_MISSING'
}
Node.js v20.5.1
Earlier versions (like Node 14) didn't support importing JSON files at all:
❯ nvm use 14
Now using node v14.21.2 (npm v6.14.17)
❯ node index.mjs
internal/process/esm_loader.js:74
internalBinding('errors').triggerUncaughtException(
^
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".json" for /tmp/sdfsdfsd/abc.json
at new NodeError (internal/errors.js:322:7)
at Loader.defaultGetFormat [as _getFormat] (internal/modules/esm/get_format.js:71:15)
at Loader.getFormat (internal/modules/esm/loader.js:105:42)
at Loader.getModuleJob (internal/modules/esm/loader.js:243:31)
at async ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:78:21)
at async Promise.all (index 0)
at async link (internal/modules/esm/module_job.js:83:9) {
code: 'ERR_UNKNOWN_FILE_EXTENSION'
}
The problem is the interaction between tsc
and Parcel.
If I bundle the example from above using tsc
(npx typescript index.ts --resolveJsonModule --esModuleInterop
), it produces:
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var abc_json_1 = __importDefault(require("./abc.json"));
console.log(abc_json_1.default);
Meanwhile, Parcel keeps this as a JSON import:
import {version as $kYxtx$version} from "appstraction/package.json";
I had a similar problem in ReportHAR.
Unfortunately, just adding the import assertion didn't help—Parcel "helpfully" strips those in the output.
In ReportHAR, I was able to work around the problem by using Parcel's bundle inlining feature to… well, inline the JSON into the bundled code.
But I don't think that that is a valid solution here. That would mean that we essentially hardcode the appstraction
version at build time. But that may not be the version that actually runs on the enduser's PC.
The problem is the interaction between
tsc
and Parcel.
Actually, that description doesn't quite describe the problem correctly. The reason that tsc
produced CJS code in this example is that I ran it with a blank tsconfig.json
and only supplied the resolveJsonModule
and esModuleInterop
via the command line.
If we repeat a similar experiment with cli
's tsconfig.json
(which is configured to output ESM: "target": "es2019"
), it will also just preserve JSON imports:
import test from './test.json';
Running the output with node
produces the same ERR_IMPORT_ASSERTION_TYPE_MISSING
error.
Whether it preserves import assertions depends on the module
option, which gets a lot more complicated. But I won't get into that, because all of this is beside the point, I was just correcting what I had said earlier.
I think the actual issue here is that the JSON import is in dependency. And tsc
will just never touch those at all. So, the tsconfig.json
settings and whatever tsc
produces, don't even matter at all.
This is turning out surprisingly hard.
npm
commands.I have seen various suggestions to read the dependency's package.json
using readFile()
but that also brings a whole host of problems:
How do I even find appstraction
's package.json
? I can't just read from CA's node_modules
because appstraction
won't be there if CA is itself a dependency in some other module:
NPM/yarn will flatten the node_modules
. But I'm not even sure if that is standardised and we can rely on that.
import.meta.resolve()
would work but according to MDN it's only supported from Node 20.6, whereas we specifically require Node 18 because of Frida.Oh, looks like someone has very kindly reimplemented the whole module resolution algorithm in a module: https://github.com/wooorm/import-meta-resolve!
While it's utterly ridiculous that we need something like that, this seems like the best solution to me.
The only other alternative that I had in mind was shipping a single .cjs
file with require('appstraction/package.json').version)
.
That seems to work.
But of course, it won't work for getting our own version. So I guess, we will still need bundle inlining. sigh
Urgh, now bundle inlining isn't working for some reason. -.-
Welp, import-meta-resolve
doesn't work either if I install CA as a dependency:
Error: Cannot find package 'appstraction' imported from /src/util.ts
Code: ERR_MODULE_NOT_FOUND
I'm wasting way too much time on this. CJS shim it is.
CJS shim doesn't work (trivially, at least) because Parcel helpfully folds that into the ESM module as:
var $e16ea7c30f77cffa$exports = {};
$e16ea7c30f77cffa$exports = {
/**
* @param {string} dependency
*
* @returns {Promise<string>}
*/ getDependencyVersion: async (dependency)=>{
if (dependency === "cyanoacrylate") dependency = "..";
return require(`${dependency}/package.json`).version;
}
};
And that obviously fails with require is not defined
.
I tried upgrading
cli
to the latest CA version. However, when trying to build, I get the following error: