Description
Projects generated by vp create vite:library include a tsconfig.json like the following:
{
"compilerOptions": {
"target": "esnext",
"lib": ["es2023"],
"moduleDetection": "force",
"module": "nodenext",
"moduleResolution": "nodenext",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
"noUnusedLocals": true,
"declaration": true,
"noEmit": true,
"allowImportingTsExtensions": true,
"esModuleInterop": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true
},
"include": ["src"]
}
The key issue is that the include field is limited to the src directory only. This causes the following problems.
Problem 1: Cannot auto-import items from src directory files outside of src
Files outside of the src directory cannot auto-import functions and other items from the src directory. For example, files in the tests directory cannot auto-import functions defined in src.
Steps to reproduce:
git clone https://github.com/mizdra-sandbox/repro-vp-lib-tsconfig-is-missing
cd repro-vp-lib-tsconfig-is-missing
code --profile-temp . (using VS Code)
- Open
tests/index.test.ts
- Place the cursor after
fn and trigger completion
- Expected behavior:
fn should be suggested
- Actual behavior:
fn is not suggested
tsserver treats files in the src directory as part of the tsconfig.json project, but treats files in the tests directory as part of an "Inferred Project". An Inferred Project is one that tsserver automatically assigns to files that have no corresponding tsconfig.json. Since tsserver cannot auto-import items between files belonging to different projects, files in tests cannot auto-import items from files in src.
Problem 2: Expected compiler options are not applied to files outside src
The tsconfig.json includes "noUnusedLocals": true. While the src directory is affected by this option, the tests directory, vite.config.ts, and similar files are not. As a result, unused local variables in those files are not reported as errors.
Steps to reproduce:
git clone https://github.com/mizdra-sandbox/repro-vp-lib-tsconfig-is-missing
cd repro-vp-lib-tsconfig-is-missing
code --profile-temp . (using VS Code)
- Open
tests/index.test.ts
- Add code containing an unused local variable (e.g.
const unused = 1;)
- Run
vp check
$ vp check
VITE+ - The Unified Toolchain for the Web
pass: All 7 files are correctly formatted (158ms, 14 threads)
error: Lint or type issues found
× typescript(TS2304): Cannot find name 'fn'.
╭─[tests/index.test.ts:3:1]
2 │ const unused = 1;
3 │ fn();
· ──
╰────
Found 1 error and 0 warnings in 3 files (140ms, 14 threads)
- No
noUnusedLocals error is reported for const unused = 1;
- Expected behavior: if
noUnusedLocals were applied, an "unused variable" error for unused should be reported in addition to the "cannot find name" error for fn
Users would reasonably expect the same compiler options applied to src to also apply to files in tests, vite.config.ts, and similar locations. This behavior is therefore likely to cause confusion.
Suggested solution
Remove the "include" field from tsconfig.json in projects generated by vp create vite:library. Omitting "include" is equivalent to specifying "include": ["**/*"], which causes all TypeScript files to be treated as part of the tsconfig.json project. This resolves both problems described above.
Alternative
None.
Additional context
About projects generated by vp create vite:application
The tsconfig.json in projects generated by vp create vite:application also contains "include": ["src"], so the same problems occur. The "include" field should be removed there as well to broaden the scope.
Compiler options used by Inferred Projects
Files in tests, vite.config.ts, and similar locations belong to an Inferred Project, so Inferred Project settings are applied to them. In tsserver, the options used are either the values sent by the editor via a setCompilerOptionsForInferredProjects request, or the default values defined in TypeScript's src/server/project.ts.
- https://github.com/microsoft/vscode/blob/8d415912364f9b29d76ae6bfc59234e59e03dbc7/extensions/typescript-language-features/src/typescriptServiceClient.ts#L622-L630, https://github.com/microsoft/vscode/blob/8d415912364f9b29d76ae6bfc59234e59e03dbc7/extensions/typescript-language-features/src/tsconfig.ts#L23
- (when tsserver version >= 5.4.0)
"module": "Preserve", "moduleResolution": "Bundler", "target": "ES2022", "allowImportingTsExtensions": true, ...
- (when tsserver version < 5.4.0)
"module": "ESNext", "moduleResolution": "Bundler", "target": "ES2022", "allowImportingTsExtensions": true, ...
- https://github.com/microsoft/TypeScript/blob/7b8cb3bdf82f400642b73173f941335775d6f730/src/server/project.ts#L590-L594, https://github.com/microsoft/TypeScript/blob/7b8cb3bdf82f400642b73173f941335775d6f730/src/services/services.ts#L1384
"module": "ESNext", "moduleResolution": "Bundler", "target": "ESNext", "allowImportingTsExtensions": false, ...
In oxlint, the values defined in the CreateInferredProjectProgram function are used.
Side effects of broadening the scope of tsconfig.json
Broadening the scope of tsconfig.json has some side effects.
Side effect 1: "module": "nodenext" and "moduleResolution": "nodenext" are applied to files outside src
The compiler options intended for src will also be applied to files in other directories. Of particular note are "module": "nodenext" and "moduleResolution": "nodenext". It is worth considering whether applying these to the tests directory, config files (e.g. vite.config.ts), and a scripts directory (e.g. https://github.com/vitest-dev/vitest/tree/main/scripts) is acceptable.
In my view, applying these options to tests and config files should not be a problem. The src directory is built by vp pack and tests is run by vp test; both are backed by Vite, which supports building code that assumes "module": "nodenext" and "moduleResolution": "nodenext".
Applying these options to config files should also be fine. Config files are either executed via Node.js Type Stripping (e.g. prettier.config.ts) or bundled before execution (e.g. vite.config.ts), and both execution models support code that assumes these module settings. The same applies to a scripts directory.
Therefore, this side effect is not a significant concern.
Side effect 2: Per-directory compiler options become harder to configure
In some cases, users may want to apply different compiler options per directory. For example, they may want "allowImportingTsExtensions": true for src and tests, but not for scripts.
If that is needed, a "solution-style" tsconfig.json can be added later to achieve per-directory configuration.
// tsconfig.json
{
"files": [],
"references": [
{ "path": "./tsconfig.default.json" }, // For `src` and `tests` directories
{ "path": "./tsconfig.scripts.json" } // For `scripts` directory
]
}
// tsconfig.default.json
{
"compilerOptions": {
"target": "esnext",
"lib": ["es2023"],
"moduleDetection": "force",
"module": "nodenext",
"moduleResolution": "nodenext",
"allowImportingTsExtensions": true
// ...
}
}
// tsconfig.scripts.json
{
"compilerOptions": {
"target": "esnext",
"lib": ["es2023"],
"moduleDetection": "force",
"module": "nodenext",
"moduleResolution": "nodenext"
// "allowImportingTsExtensions": true,
// ...
}
}
Since a workaround exists, this side effect is not a significant concern.
Validations
Description
Projects generated by
vp create vite:libraryinclude atsconfig.jsonlike the following:{ "compilerOptions": { "target": "esnext", "lib": ["es2023"], "moduleDetection": "force", "module": "nodenext", "moduleResolution": "nodenext", "resolveJsonModule": true, "types": ["node"], "strict": true, "noUnusedLocals": true, "declaration": true, "noEmit": true, "allowImportingTsExtensions": true, "esModuleInterop": true, "isolatedModules": true, "verbatimModuleSyntax": true, "skipLibCheck": true }, "include": ["src"] }The key issue is that the
includefield is limited to thesrcdirectory only. This causes the following problems.Problem 1: Cannot auto-import items from
srcdirectory files outside ofsrcFiles outside of the
srcdirectory cannot auto-import functions and other items from thesrcdirectory. For example, files in thetestsdirectory cannot auto-import functions defined insrc.Steps to reproduce:
git clone https://github.com/mizdra-sandbox/repro-vp-lib-tsconfig-is-missingcd repro-vp-lib-tsconfig-is-missingcode --profile-temp .(using VS Code)tests/index.test.tsfnand trigger completionfnshould be suggestedfnis not suggestedtsserver treats files in the
srcdirectory as part of thetsconfig.jsonproject, but treats files in thetestsdirectory as part of an "Inferred Project". An Inferred Project is one that tsserver automatically assigns to files that have no correspondingtsconfig.json. Since tsserver cannot auto-import items between files belonging to different projects, files intestscannot auto-import items from files insrc.Problem 2: Expected compiler options are not applied to files outside
srcThe
tsconfig.jsonincludes"noUnusedLocals": true. While thesrcdirectory is affected by this option, thetestsdirectory,vite.config.ts, and similar files are not. As a result, unused local variables in those files are not reported as errors.Steps to reproduce:
git clone https://github.com/mizdra-sandbox/repro-vp-lib-tsconfig-is-missingcd repro-vp-lib-tsconfig-is-missingcode --profile-temp .(using VS Code)tests/index.test.tsconst unused = 1;)vp checknoUnusedLocalserror is reported forconst unused = 1;noUnusedLocalswere applied, an "unused variable" error forunusedshould be reported in addition to the "cannot find name" error forfnUsers would reasonably expect the same compiler options applied to
srcto also apply to files intests,vite.config.ts, and similar locations. This behavior is therefore likely to cause confusion.Suggested solution
Remove the
"include"field fromtsconfig.jsonin projects generated byvp create vite:library. Omitting"include"is equivalent to specifying"include": ["**/*"], which causes all TypeScript files to be treated as part of thetsconfig.jsonproject. This resolves both problems described above.Alternative
None.
Additional context
About projects generated by
vp create vite:applicationThe
tsconfig.jsonin projects generated byvp create vite:applicationalso contains"include": ["src"], so the same problems occur. The"include"field should be removed there as well to broaden the scope.Compiler options used by Inferred Projects
Files in
tests,vite.config.ts, and similar locations belong to an Inferred Project, so Inferred Project settings are applied to them. In tsserver, the options used are either the values sent by the editor via asetCompilerOptionsForInferredProjectsrequest, or the default values defined in TypeScript'ssrc/server/project.ts."module": "Preserve","moduleResolution": "Bundler","target": "ES2022","allowImportingTsExtensions": true, ..."module": "ESNext","moduleResolution": "Bundler","target": "ES2022","allowImportingTsExtensions": true, ..."module": "ESNext","moduleResolution": "Bundler","target": "ESNext","allowImportingTsExtensions": false, ...In oxlint, the values defined in the
CreateInferredProjectProgramfunction are used."module": "ESNext","moduleResolution": "Bundler","target": "ES2022","allowImportingTsExtensions": true, ...Side effects of broadening the scope of
tsconfig.jsonBroadening the scope of
tsconfig.jsonhas some side effects.Side effect 1:
"module": "nodenext"and"moduleResolution": "nodenext"are applied to files outsidesrcThe compiler options intended for
srcwill also be applied to files in other directories. Of particular note are"module": "nodenext"and"moduleResolution": "nodenext". It is worth considering whether applying these to thetestsdirectory, config files (e.g.vite.config.ts), and ascriptsdirectory (e.g. https://github.com/vitest-dev/vitest/tree/main/scripts) is acceptable.In my view, applying these options to
testsand config files should not be a problem. Thesrcdirectory is built byvp packandtestsis run byvp test; both are backed by Vite, which supports building code that assumes"module": "nodenext"and"moduleResolution": "nodenext".Applying these options to config files should also be fine. Config files are either executed via Node.js Type Stripping (e.g.
prettier.config.ts) or bundled before execution (e.g.vite.config.ts), and both execution models support code that assumes these module settings. The same applies to ascriptsdirectory.Therefore, this side effect is not a significant concern.
Side effect 2: Per-directory compiler options become harder to configure
In some cases, users may want to apply different compiler options per directory. For example, they may want
"allowImportingTsExtensions": trueforsrcandtests, but not forscripts.If that is needed, a "solution-style"
tsconfig.jsoncan be added later to achieve per-directory configuration.Since a workaround exists, this side effect is not a significant concern.
Validations