Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support jsconfig.json #87

Closed
MichaelDeBoey opened this issue Apr 14, 2023 · 20 comments
Closed

Support jsconfig.json #87

MichaelDeBoey opened this issue Apr 14, 2023 · 20 comments
Labels
enhancement New feature or request

Comments

@MichaelDeBoey
Copy link

jsconfig.json is the same file as tsconfig.json, it just says you're using JS instead of TS

@aleclarson's vite-tsconfig-paths used to use tsconfig-paths to resolve the jsconfig.json/tsconfig.json, but switched to this package for doing so.

This however had the breaking change of not supporting jsconfig.json anymore (see aleclarson/vite-tsconfig-paths#22 (comment)).

@dominikg I would love to get support for jsconfig.json again, as we're converting tsconfig.json to jsconfig.json if people choose to have JS templates in @remix-run.
This means our stacks (see indie-stack, blues-stack & grunge-stack) needs to keep using vite-tsconfig-paths v3 in order to keep supporting this behavior, but of course we'd love to have all our dependencies to latest version.

I'm always happy to help implement this as well if you could point me into the right direction.

@MichaelDeBoey MichaelDeBoey added the enhancement New feature or request label Apr 14, 2023
@dominikg
Copy link
Owner

This is the most prominent documentation of jsconfig i could find: https://code.visualstudio.com/docs/languages/jsconfig, but it remains vague about

  • is jsconfig.json really just tsconfig.json with implicit "allowJs": true
  • what would be the expected resolve of "extends":"@tsconfig/node18" (or any other node resolution style thing. would it look for tsconfig.json or jsconfig.json or both)
  • is a jsconfig.json allowed to extend a config with "allowJs": false, and it override that to true?
  • how would a project with mixed jsconfig.json/tsconfig.json files work
  • do check tools actually resolve aliases defined in jsconfig.json (which would make that js code not work until bundled)
  • can .js files have a tsconfig.json as parent or would it ignore them to find the first jsconfig.json or is it the first with allowJs: true and matching file

As for implementation, you'd have to check for existing use of hardcoded "tsconfig.json" values and refactor them to allow using "jsconfig.json".
To avoid a breaking change in the public api or lots of new exports, it should be an option that defaults to false.
You'd also have to start checking code file extensions to find the right parent config.

And last but not least there would have to be additional test fixtures that ensure it's working correctly.

@aleclarson
Copy link

is jsconfig.json really just tsconfig.json with implicit "allowJs": true

Here's what the source code says: https://github.com/microsoft/TypeScript/blob/bdcf8abb0c280cd0e41a2bc1f9c5f806b05ae424/src/compiler/commandLineParser.ts#L3394-L3399

function getDefaultCompilerOptions(configFileName?: string) {
    const options: CompilerOptions = configFileName && getBaseFileName(configFileName) === "jsconfig.json"
        ? { allowJs: true, maxNodeModuleJsDepth: 2, allowSyntheticDefaultImports: true, skipLibCheck: true, noEmit: true }
        : {};
    return options;
}

@duanwilliam
Copy link

duanwilliam commented Jul 28, 2023

i've made a fork at https://github.com/duanwilliam/tsconfck/tree/jsconfig-support to try to work on it.
so far it addresses the following points:

  • is jsconfig.json really just tsconfig.json with implicit "allowJs": true
  • is a jsconfig.json allowed to extend a config with "allowJs": false, and it override that to true?

seems it can in fact override the default options (e.g. the jsconfig can set "allowJs": false), at least as far as ts.parseJsonConfigFileContent is concerned.

  • how would a project with mixed jsconfig.json/tsconfig.json files work
  • can .js files have a tsconfig.json as parent or would it ignore them to find the first jsconfig.json or is it the first with allowJs: true and matching file

ts.findConfigPath seems to first search for a tsconfig, and only searches for a jsconfig if no tsconfig was found. microsoft/TypeScript#15869 (comment) seems to reaffirm that the tsconfig is always preferred if both are present somewhere in the ancestor tree.

  • do check tools actually resolve aliases defined in jsconfig.json (which would make that js code not work until bundled)

(i'm assuming you're referring to compilerOptions.paths here?) yes, and i believe this is the case with ts files as well (just using tsc with path aliases means the transpiled js code does not work until bundled, since the aliases are preserved as is) - does there need to be anything done regarding this?

@dominikg
Copy link
Owner

Thanks for looking into this!

seems it can in fact override the default options (e.g. the jsconfig can set "allowJs": false), at least as far as ts.parseJsonConfigFileContent is concerned.

this is a bit silly. jsconfig.json with "allowJs": false would be moot, but from an implementation point it makes it a bit easier as we don't need a special case. just that we have to add the default options in the exact same way as ts would do.

tsconfig is always preferred if both are present somewhere in the ancestor tree.

is this regardless of allowJs and includes ? For ts, it continues up the tree if the closest tsconfig doesn't include the file. So for a js file, would it stop only if tsconfig includes the js file (and does allowJs implicitly add js extensions to globs?) or would it have to be explicitly included. Again we have to follow the behavior of typescript here. (tsconfck glob handling)

does there need to be anything done regarding this
Again depends on how it works with tsc - and in this case without it too.
foo.js

import bar from '$somewhere/bar.js'

jsconfig.json

{
  "compilerOptions": {
    "paths": {"$somewhere":"path/to/somewhere"}
  }
}

If i understand you correctly you're saying that bundlers are aware of jsconfig.json compilerOptions.paths and resolve them even without tsc? My understanding is that you would have to run tsc first to resolve these, bundlers don't do it on their own (otherwise they'd had have a way to resolve these correctly already)

@duanwilliam
Copy link

duanwilliam commented Jul 29, 2023

we have to add the default options in the exact same way as ts would do.

as far as i'm aware the work on my fork is doing so at the moment.
default options are always the first keys of compilerOptions; and if compilerOptions is not defined, it gets added to the end of the tsconfig object.

For ts, it continues up the tree if the closest tsconfig doesn't include the file. So for a js file, would it stop only if tsconfig includes the js file

i can't seem to observe this, are there any specific tests demonstrating it?

(and does allowJs implicitly add js extensions to globs?) or would it have to be explicitly included. Again we have to follow the behavior of typescript here.

(wip)

If i understand you correctly you're saying that bundlers are aware of jsconfig.json compilerOptions.paths and resolve them even without tsc? My understanding is that you would have to run tsc first to resolve these, bundlers don't do it on their own (otherwise they'd had have a way to resolve these correctly already)

compilerOptions.paths is just a declaration of mappings that exist (https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping), TS itself doesn't transform them in any way: see microsoft/TypeScript#9910 (comment). the actual alias resolution is left to loaders/bundlers, of which often have a plugin to define aliases based on those read from the tsconfig.
or to summarize with a snippet commonly shared in the TS discord,
"The paths and baseUrl compiler options don't cause any remapping of imports paths, they only inform TS of existing mappings, which you'll have to setup with some other tool"

so (as far as i'm aware) handling jsconfig.json compilerOptions.paths is in the realm of the bundlers/loaders and outside the scope of TS, which just assumes that those paths declared in the config do exist and will be resolved as defined in the option.

@dominikg
Copy link
Owner

@MichaelDeBoey tsconfk@3 is currently in pre-release and will be released soon (to be included in vite5) are you still interested in this?

@dominikg
Copy link
Owner

I did look into this a bit more and it seems like tsc itself does not look for jsconfig.json unless you explicitly tell it to via -p path/to/jsconfig.json

https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#using-tsconfigjson-or-jsconfigjson

findConfigFile also has an explicit argument that defaults to tsconfig.json and if you set that to jsconfig.json tsconfig.json files are ignored.

So to line up with typescript the best way to implement this in tsconfck is to add a new option configName that defaults to tsconfig.json and can be set to jsconfig.json as well.

In addition the extensions handling needs to be updated to include js/cjs/mjs/jsx extensions in case allowJs is true

But this won't help vite-tsconfig-paths on it's own as that would have to detect use of jsconfig.json and call tsconfck accordingly.

In general i think vite-tsconfig-paths maybe has it backwards. Instead of making tsconfig/jsconfig the source of truth for aliases, vite config should remain that source and vite-tsconfig-paths can generate a tsconfig.vite-paths.json in configResolved that users can extend in their own configs to get the aliases. Thanks to extends as an array this should not be too hard to do. One thing to keep in mind though is that compilerOptions.paths is not merged, so user configs must not define other paths then. (Which i think is fine, after all it should be vite doing it).

some features of ts path mapping like fallbacks would not be easily replicated by this, but i don't think they are supported by vite-tsconfig-paths or vite today (unless you implement a custom plugin with resolveId that runs this.resolve multiple times)

cc @aleclarson

@MichaelDeBoey
Copy link
Author

@dominikg Since we moved away from converting Remix stacks to JS in v2, we're just going to update to latest vite-tsconfig-paths
It would be nice to still get jsconfig.json support though for people who want to fork the official TS stacks and make it a JS stack

@dominikg
Copy link
Owner

see #132

@dominikg
Copy link
Owner

dominikg commented Oct 1, 2023

released in tsconfck@3.0.0-next.9

import {find,parse} from 'tsconfck'
const jsconfigPath = await find('some/path/to/a/file.js',{configName:'jsconfig.json'}) 
// '/abs/some/path/jsconfig.json'

const parsed =  await parse('some/path/to/a/file.js',{configName:'jsconfig.json'})
// parsed = {
//    tsconfigFile: '/abs/some/path/jsconfig.json'
//    tsconfig: {...}
// }

cli also has a -js arg now to turn on configName: 'jsconfig.json'

npx tsconfck@next find some/path/to/a/file.js -js

@aleclarson
Copy link

I did look into this a bit more and it seems like tsc itself does not look for jsconfig.json unless you explicitly tell it to via -p path/to/jsconfig.json

I'd say that vite-tsconfig-paths aims to mimic VS Code's tsserver client more than the tsc command line tool. The subtle difference is that VS Code doesn't require explicit configuration for jsconfig.json support.

@dominikg
Copy link
Owner

dominikg commented Oct 2, 2023

In that case vite-tsconfig-paths would have to reimplement whatever logic vscode uses internally, with the new configName option you have the ability to do so. tsconfck itself won't follow vscode but tsc.

Note: you can use a single cache instance for both tsconfig and jsconfig via

const cache = new TSConfckCache();
const jsconfig = await find(file,{cache, configName:'jsconfig.json'});
const tsconfig = await find(file,{cache});

// or
const [jsconfig,tsconfig] = await Promise.all(['jsconfig.json','tsconfig.json'].map(configName => find(file,{cache,configName}));

// see which one is closer and act accordingly

@blikblum
Copy link

blikblum commented Oct 2, 2023

IMO is enough to vite-tsconfig-paths expose configName option

@aleclarson
Copy link

I plan for vite-tsconfig-paths to use Vite's internal tsconfck cache at some point, so it would be great if @patak-dev chimed in with his perspective.

@dominikg
Copy link
Owner

dominikg commented Oct 3, 2023

you can do that with vite providing vite-tsconfig-paths tsconfig.json, but bring your own cache (and watcher) for jsconfig.json. Neither tsc nor esbuild take jsconfig.json into account by default, so it would be really strange for tsconfck to do so

@patak-dev
Copy link
Sponsor

I trust @dominikg's call on this one, I don't have a particular perspective to add here.

@aleclarson
Copy link

Neither tsc nor esbuild take jsconfig.json into account by default, so it would be really strange for tsconfck to do so

The difference is that tsc/esbuild are for compiling TypeScript files, while tsconfck is for tsconfig loading and, as a consequence, bundling, which is not solely TypeScript focused (a situation similar to VS Code).

@dominikg
Copy link
Owner

dominikg commented Oct 3, 2023

I'm not keen on adding custom find modes that implement what various other tools are doing. tsconfck is already more complex than i'd like it to be, thanks to the myriad of extras typescript added to it's config handling over the years.

What exactly would you expect tsconfck to return if not the config that tsc itself would use? The new configName option is exactly what they do in their own findConfigFile function.

@dominikg
Copy link
Owner

dominikg commented Oct 3, 2023

tbh their config system is too complex in general and i hope they revise it, maybe in a similar way eslint now offers a flat config, making tsconfck obsolete.

@dominikg
Copy link
Owner

Given that the new async implementation is very fast and the code required in userland to implement resolving either jsconfig or tsconfig is pretty small and working for astro, i'm calling this implemented.

@aleclarson if this still isn't enough for vite-tsconfig-paths please raise a new feature request for mixed configName support, including links to typescript and vscode documentation describing in detail how it's supposed to work correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants