Taming Visual Studio Code and Intellisense in Monorepos w/Typescript

Posted Saturday, March 1, 2025 by Sri. Tagged MEMO, VSCODE
EDITING PHASE:gathering info...

Development Environment within Visual Studio Code

Root Folder

This is the "host environment" that can make use of direct import of ursys/core, ursys/addons and call the various build utilities.

  • Uses gitignore on directories beginning with app-* and sna-*, which can contain importers of ursys/core from file:../_ur and file:../_ur_addons during URSYS core expansion of features to support them.
  • Has VSCODE environment configuration and a command runtime environment for utility tasks
  • Has main export for @ursys/core and @ursys/addons for use with npm install git+https://github.com/dsriseah/ursys.git for external consumers of the library

Core Developers

This is the package ursys/core, with source located in _ur subdirectory

  • Typescript with Intellisense within the Monorepo when developing Core within _ur directory
  • Shared .ts files in _ur/common
  • Node version of core in _ur/node-server with @node-index.mts as the main export
  • Web version of core in _ur/web-client with @client-index.ts as the main export
  • Package export for _ur/package.json points to the runtime version of built packages and types

Addon Developers

This is the package ursys/addons

  • Core Library package @ursys/core** for both node and web platforms in _ur` directory
  • Addons that import from ursys/core in _ur_addons and project roots, with full typescript

Questions and Assumptions

Q. Do I understand how to define a module like @ursys/core and import from it?

The use of @ursys designates a package namespace, which makes it impossible to export both @ursys/core and @ursys/addons installable from git+https://github.com/dsriseah/ursys.git.

So, we drop the @ursys in favor of plain ursys as the package name in the root's package.json.

For separation, we can use module subdirectories as internal unpublished packages in our npm workspace, core and addons. The root package.json would then be something similar to:

ursys root package.json

{
"name": "ursys",
"type": "module",
"exports": {
".": "./core/index.js",
"./addons": "./addons/index.js"
},
"workspaces": ["_ur/core", "_ur/addons"]
}

The internal packages package.json looks like:

ursys/core package.json

{
"name": "ursys",
"private": true,
"type": "module",
"exports": {
".": "./index.js",
"./addons": "../_ur_addons/index-addons.js"
}
}

ursys/addons package.json

{
"name": "ursys-addons",
"private": true,
"type": "module",
"exports": {
".": "./index.js"
},
"dependencies": {
"ursys": "file:../_ur"
}
}

The external consumers of the ursys and ursys-addons modules

Q. What import and package definitions are used?

CORE - The main @web-client.ts and @node-server.mts files export the main library for each respective platform. When adding files to _ur (aka core), they will use file-based relative imports.

ADDONS - Source files for addons are built on core, and reside inside the _ur_addons directory. The ursys dependency is defined as ursys:file:.._ur and imported as import * as UR from 'ursys' as a module import. Addons can not import from each other.

INTERNAL APPS - Source files for apps live in the URSYS root directory, and use file-based relative imports. This is the case for when new addons and features are being developed. The dependency is ursys:file:.. (root dir).

EXTERNAL APPS - These apps live outside of the ursys root directory and use module imports. This is the case for external consumers of the URSYS library. The dependency is ursys:git+https://github.com/dsriseah/ursys.git and its imported as import * as UR from 'ursys' and import * as ADDONS from 'ursys/addons'.

Q. Do I know how Typescript Intellisense Resolution works inside of VSCODE?

I believe that it relies on the module import itself and local tsconfig.json files. In the case where the module import has multiple platforms defined in exports, then tsconfig.json is used per-directory to set the expected platform for each code base by setting compilerOptions.

Typescript looks in the "closest parent directory" for tsconfig.json. This is a suggested arrangement according to ChatGPT "Deep Research"

/repo-root

  package.json          # exports for outside consumers
  tsconfig-base.json    # project-wide settings only (no includes)
  tsconfig.json         # autofound by vscode, includes root sna, app, example-

  _ur/                  # packages directory
  | core/               # ursys module
  |   package.json      # type:module
  |   tsconfig.json     # extend tsconfig-base with local includes
  | addons/            
  |   package.json      # type:module
  |   tsconfig.json     # extend tsconfig-base with local includes
  | common/
  |   tsconfig.json     # extend tsconfig-base with local includes

  example-app/
  |   package.json      # ursys:git+https://github.com/.../ursys.git, type:module
  |   tsconfig.json     # ???

To enforce typescript using the esm declarations, package.json should have type:module set, otherwise it might default to using the commonjs declarations for Intellisense.

Do I know how to create type modules?