isaacs / tshy

Other
869 stars 17 forks source link

How to include JSON files? #73

Closed jrnail23 closed 3 months ago

jrnail23 commented 3 months ago

I've got a directory containing a bunch of JSON files for our locale-specific content translations (e.g., translations/en-US.json), and in my usual tsconfig file, I include those files like this: "include": ["src", "src/**/*.json"]. Now that I'm trying to introduce tshy into my repo, the tshy command fails with this:

src/localization/messages.ts:1:18 - 
error TS6307: File '/my-repo/packages/my-package/src/translations/en-US.json' is not listed within the file list of project '/my-repo/packages/my-package/.tshy/esm.json'.
Projects must list all files or use an 'include' pattern.

Screenshot 2024-06-06 at 11 29 32 PM

Not entirely unexpected, of course, because tshy hijacks include in tsconfig.json, and AFAIK, *.json files must be explicitly included in tsconfig. BTW, I have "resolveJsonModule": true in my compilerOptions.

Is there a way I can get tshy to include these JSON files?

isaacs commented 3 months ago

This is maybe a dumb question, but could you just not put them in src? All that tsc does with json is basically just copy it. (But slower; it reads it, parses it, then re-stringifies it with 4-space indentation.) If you put it in a different folder, then your build would be ever so slightly faster, and it wouldn't be an issue.

That said, seems to pretty much work fine for me, with the caveat that node won't let you import it into ESM without with { type: 'json' }, but TSC will complain about compiling that to CommonJS, since the module type assertion doesn't make sense for require(). If that's how you're loading it, you could work around that issue with a //@ts-ignore on the line where you're importing them, or by using dynamic imports. But in either case, if you're importing JSON, you get that annoying node warning about it.

Even if it's not in include, anything that's imported by any included module in the tree will be included in the build.

https://github.com/isaacs/tshy-json-example/tree/main

In this example you can see that blah.json and json.json get copied over just fine, but the src/ignored.json doesn't.

Typically, I try to avoid importing JSON (at least, until the experimental warnings go away) and instead just JSON.parse(readFileSync(path, 'utf8')). To get the path from a consistent location, you can use https://www.npmjs.com/package/package-json-from-dist and then resolve from there, since that'll give you the folder root.

If you have a repro, and the example here doesn't make it obvious what the difference is, I'd be happy to take a look.

jrnail23 commented 3 months ago

So one of the things here is that by including these JSON files in the TypeScript compilation, we actually get strong typing for our resource keys.

Even if it's not in include, anything that's imported by any included module in the tree will be included in the build.

Interesting. I'll play with this and see what I can do with it.

jrnail23 commented 3 months ago

@isaacs, you mentioned this:

with the caveat that node won't let you import it into ESM without with { type: 'json' }, but TSC will complain about compiling that to CommonJS, since the module type assertion doesn't make sense for require().

With your example repo, I don't seem to have a problem running tshy or executing the program when omitting with { type: 'json' }. Is there something I'm missing? FWIW, this is on Node v20.13.0, but it also seems fine in Node v18.16.0.

UPDATE: ah, ok, my bad. when I force it to run ESM it blows up with ERR_IMPORT_ASSERTION_TYPE_MISSING

isaacs commented 3 months ago

So one of the things here is that by including these JSON files in the TypeScript compilation, we actually get strong typing for our resource keys.

Yes, true, when you import them, it can infer the types.

But that would be the case even if they're not being "compiled" (ie, copied and reformatted) by tsc. When you JSON.parse yourself, it's just any unless you typecast it yourself.

If you can work around the import assertion issues (eg, by including the assertion and having a //@ts-ignore on the line before the import), and you're ok with tsc making multiple copies of the files, then it should work fine. Any JSON files that are imported will be included by default. Without looking at the code myself, I'm not sure why you're getting that error. Can you share the code? (Or if not, a simplified reproduction?)

It might not be a bad idea to just include *.json in tshy's include directive. The only reason it hijacks it is so that we don't get .mjs in dist/commonjs and .cjs in dist/esm.

isaacs commented 3 months ago

Ohh, I remember why it doesn't include *.json: because tshy is creating and deleting {"type":"module"} and {"type":"commonjs"} in src/package.json to set the dialect for each build.

So maybe it could add src/**/!(package).json, to include any other kind of json file?