How to specify the distributable directory visible for both NPM and TypeScript? (Multiple files case) - node.js

The library written in TypeScript includes three main files for distribution:
NodeJS.js - for, obviously, Node.js runtime.
BroswerJS.js - for, obviously, browser runtime.
index.js - common functionality for both browser and Node.js
There no "main" file in this library so I has not specified this property in package.json.
Planning usage:
import { isUndefined, isNull } from "package-name;
import { delegateClickEventHandling } from "package-name/BrowserJS;
import { NodeJS_Timer } from "package-name/NodeJS;
Currently, the TypeScript with below config compiles files below Source directory to Distributable directory:
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"moduleResolution": "Node",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"removeComments": true,
"outDir": "Distributable/",
"declaration": true
},
"include": [ "Source/**/*" ]
}
If to publish the library such as, TypeScript even will not see it:
import { isUndefined } from "package-name";
TS2307: Cannot find module 'package-name' or its corresponding type declarations.
Because as default TypeScript expecting that .d.ts files will be in root directory of the library. But the distributables are in Distributable directory!
And of course, isUndefined will not be found. I know about "main" property in package.json, but it is for one file case, but what about directory?
I know that multiple distributable files exporting is the supported scenario. For example the mysql2 exporting promise.ts besides index.js:
import MySQL from "mysql2";
import MySQL_Promise from "mysql2/promise";
Update
The NPM part solved - modern solution is exports filed in package.json:
"exports": {
".": "./Distributable/index.js",
"./NodeJS": "./Distributable/NodeJS.js",
"./BrowserJS": "./Distributable/BrowserJS.js"
},
But distribution files are still invisible for TypeScript.
TS2307: Cannot find module 'package-name' or its corresponding type declarations.
I learned about "types" field of package.json. Unfortunately, it could be only a string. It means currently it's impossible to specify multiple files. The issue about making in to array has been declined.
But how to make visible all of "./Distributable/index.js", "./Distributable/NodeJS.js", "./Distributable/BrowserJS.js" for TypeScript?
Please don't suggest me again to make all imports to single entry point. In this question we considering the multiple entry points case.

I am not entirely sure what you are trying to achive, in TS generally when you have single project with 1 configuration file, and you emit multiple files from it, you would not use package name within the same project, use path instead './someFileName'.
If you have multiple projects (tsconfig files) to manage different directories - sort of monorepo thing going on.
Your best options is project references: https://www.typescriptlang.org/docs/handbook/project-references.html
Or if you are doing something else then this may help altho I'd do this as last resort :-)
https://www.typescriptlang.org/tsconfig#paths

Related

Persistent undefined error in typescript import export

There's already a LOT of questions about typescript in multiple files.. for instance, this one,
Typescript import/export
Interesting question and answer, I simplified and tested it, see below.. but whatever I try, I still get
Uncaught TypeError: Cannot read properties of undefined (reading 'A')
.. as does any other example of import/export in TypeScript I found online. Whatever I do, whatever object I try export (class, function, const) with or without using a module: I get the same error.
Maybe there is something wrong in my NPM/TSC/React configuration ? Should I change e.g. tsconfig.js when i want to use more than one typescript file in a project ? I'm lost, what do I miss ?
tsconfig.json
{ // TypeScript configuration file: provides options to the TypeScript
// compiler (tsc) and makes VSCode recognize this folder as a TS project,
// enabling the VSCode build tasks "tsc: build" and "tsc: watch".
"compilerOptions": {
"target": "es5", // Compatible with older browsers
"module": "umd", // Compatible with both Node.js and browser
"moduleResolution": "node", // Tell tsc to look in node_modules for modules
"sourceMap": true, // Creates *.js.map files
"jsx": "react", // Causes inline XML (JSX code) to be expanded
"strict": true, // Strict types, eg. prohibits `var x=0; x=null`
"alwaysStrict": true // Enable JavaScript's "use strict" mode
},
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
first.tsx
const A ={
val: 'A'
}
export { A }
app.tsx
import { A } from "./first";
// ... other code
function reportPerson()
{
console.log(A);
}
.. Both files translate to .js with TSC, but A is reported by the Google Chrome console as undefined,
Both tsx files are in the same directory, TSC converts them both to JS without any issue.
What's going on ?
Thanks everyone for the advice (I didn't solve the above minimal example either..)
In order to properly link my stuff together, I've now put Parcel 2 to work,
https://www.npmjs.com/package/parcel
npm i parcel
This is basically a bundler, that allows separate ts files to be concatenated after they are compiled to Javascript and it will put everything in a \dist directory,
parcel build src/index.html
Based on a small react example, I put my first "modulized" little app in TypeScript to work. Then, with the help of expert advise, I proceeded with twgl.js, which is a great toolkit for Webgl2.
npm install twgl.js
This javascript library even has sub-modules.. and everything links fine now, I can access (all of?) twgl with
import * as twgl from "./twgl-full.js";

Typescript Establishing Absolute Imports Node

I built a project in node using TS, and I'm trying to implement absolute path for imports.
However, when I run the project it begins failing saying.
[1] Error: Cannot find module 'src/common/logger'
[1] Require stack:
[1] - C:\...xyz\Workspaces\PROJECT\dist\index.js
When I switch 'src/common/logger' to './common/logger' it proceeds to point to other ones that absolute path imports.
I figure its obviously something wrong with my TS config, but I can't seem to see where I am messing up.
Ultimately I want the imports to become 'common/xyz' and 'db/xyz'
My project directory follows this flow:
├───tsconfig.json
├───node_modules
├───dist // output location
└───src
|───common
|───db
└───index.ts
{
"compilerOptions": {
/* Language and Environment */
"target": "es2021",
/* Modules */
"module": "commonjs",
"rootDir": "./src",
"moduleResolution": "node",
"baseUrl": ".",
"paths": {
"src/*": ["./src/*"],
"/*": [ "./src/*" ],
},
"outDir": "./dist"
}
}
My thought is that the issue is how I set up the tsconfig.
I tried looking through the documentation, but I clearly am not understanding the paths section. I've tried several changes to the paths, but still can't seem to get it work.
Any help would be greatly appreciated.
Leaving this bread trail for people who come across this issue.
TLDR; There is no way with tsconfig only to do it. You would need to use packages such as Webpack, Gulp, or other npm packages to help resolve the absolute imports.
My advice:
If you are working on just getting the project up and running, stick with relative imports for now. Then you can look into the 3rd party tools to implement the absolute pathing.

vscode not finding type hinting for npm mssql via #types/mssql

I've been beating my head against a wall for hours on this one, it should be simple. I have a project where the node_modules are installed in a nested folder. This is a Node project with a tsconfig.json in the project root. It's not a typescript project, but I can't use typeRoots in jsconfig.json so I'm forced to use a tsconfig.json. I'm trying to get vscode to provide type hinting for the mssql package, but no matter what happens it's showing up in vscode as type any. I'm able to properly get type hinting for mocha (describe, it), node (assert), and the functions declared within the project. I'm getting type hinting for the p-memoize project when I require it (that uses d.ts file located in the project, not in #types). The difference here is that with mssql I'm using require() and it's failing with that.
Here is my tsconfig.json:
{
"compilerOptions": {
"baseUrl" : ".",
"allowJs": true,
"module": "commonjs",
"moduleResolution": "node",
"noEmit": true,
"typeRoots": ["containers/shared/node_modules/#types/"],
"paths" : {
"*": ["containers/shared/node_modules/*"],
"#simpleview/*" : ["../../containers/*"]
}
}
}
Then I have a code file with the following:
const mssql = require("mssql");
mssql is resolving as type any.
In my package.json I have the latest version of both mssql and #types/mssql:
"#types/mssql": "4.0.7",
"mssql": "6.2.1",
Am I doing something wrong here or is the #types/mssql project bugged, but if so, what exactly is wrong with it so I could submit a PR to fix it?

Use non-relative imports using TypeScript's baseurl property without WebPack

I am writing a node web application using TypeScript and Express.
I managed to get everything working, however the issue I have run into is that my imports don't seem to respect the baseUrl option of my tsconfig.json.
Here is how my tsconfig.json looks like:
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"pretty": true,
"sourceMap": true,
"target": "es6",
"outDir": "./dist",
"baseUrl": "./src"
},
"exclude": [
"node_modules"
]
}
As an example, let's say I have the following files:
- dist/
- a/
- car.js
- b/
- hat.js
In car.js I can easily require hat.js by doing:
import hat from '../../b/hat'; // relative version
This works as expected.
However, I also want to be able to do the following:
import hat from 'b/hat'; // absolute version
This does not generate any issues during compilation or shows any IDE errors, as the tsconfig.json specifies the baseUrl as ./src. Thus the above is perfectly valid TypeScript code.
However, my expectation was that the code will compile down to the relative version:
const hat = require('../../b/hat');
Unfortunatly it compiled down to:
const hat = require('b/hat');
and thus predictably does not work.
Other users have solved this issue by using 3rd party tools such as: https://github.com/s-panferov/awesome-typescript-loader
https://decembersoft.com/posts/say-goodbye-to-relative-paths-in-typescript-imports/
But majority of these tools is designed to work with WebPack, which isn't really suitable for an node back-end application. This is because we are running a long-running server, and thus won't benefit from being bundled into a single file versus several different files (unlike front-end web development).
My question is, how can I compile my TypeScript files, without WebPack, so that absolute imports works correctly.
So the way to do it is to transform back the non-relative imports to relative imports after build so that commonjs will recognize them. And the way to easily do it is the following:
Step 1: Install the transform modules:
npm i -D typescript-transform-paths
npm i -D ttypescript
Step 2: Modify tsconfig.json
"compilerOptions: {
...
"plugins": [
{ "transform": "typescript-transform-paths" },
{ "transform": "typescript-transform-paths", "afterDeclarations": true }
]
}
Step 3: Modify package.json
We will use ttypescript to build the project instead of tsc inorder to use transformers.
"build": "ttsc"
Step 4: Build and Start the project
npm run build
npm start
OP I haven't gotten this to work yet, but this article may provide some guidance: https://levelup.gitconnected.com/get-rid-of-relative-import-path-hell-in-your-typescript-project-9952adec2e84
TL;DR
yarn add link-module-alias
package.json
...
"_moduleAliases": {
"~": "dist"
}
...
My question is, how can I compile my TypeScript files, without WebPack, so that absolute imports works correctly.
My opinion
Don't. In fact don't do it even do it with webpack.
Reason
The node resolution algorithm is complicated enough : https://nodejs.org/api/modules.html#modules_all_together and doesn't need to be complicated further with "and oh, now we have baseurl".
If you still want to do it, here are your options.

Exclude node_modules directory for TypeScript compilation, but allow one individual subdirectory

The whole node_modules directory can be completely excluded for TypeScript compilation using this tsconfig.json:
{
"compilerOptions": {
"module": "commonjs",
"sourceMap": true,
"target": "es6"
},
"exclude": [
"node_modules"
]
}
But how can I allow one individual sub-directory under node_modules, like node_modules/app, to still get compiled? I know that the files section can be used to specify individual files and override the exclude section, but that can get unwieldy very quickly, especially when used alongside exclude. Is there a better option?
For a bit of context, I am planning to put application-specific modules in the node_modules/app directory, so I can require them without using relative paths:
var m = require("app/module1")
versus
var m = require("../../module1")).
Just require the module/subfolder that contains your code. Explicitly importing a file/directory overrides the global "exclude" in a sense that it will be picked up by tsc.
Also set "moduleResolution": "node" if you haven't already.

Resources