Building Fullstack Application with ts-node - node.js

I am currently having a problem in the build process of my typescript application.
Currently this application is an api using the express framework.
It is run in development mode via ts-node and compiled with tsc:
npm run dev
node --inspect=0.0.0.0:9229 -r ts-node/register -r tsconfig-paths/register ./src/index.ts
npm run build
tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json --silent
I now want to integrate a react ssr client to this application server.
So far no problem my first solution was to make a monorepo style architecture with different packages.json and build my react application independently then give the generated statics files to express.
The problem appeared when integrating react-router into my architecture.
Indeed I can no longer just serve static files I need to serve a static router from react router.
I'm not going to dwell on why I need my application to be isomorphic (I leave you this more than complete comment to answer the question: https://stackoverflow.com/a/36623117/11312725) but when I 'integrate my react to my server the build no longer works.
Whether it's ts-node or tsc, the build is down..
tsconfig.json
{
"compilerOptions": {
// Project options
"allowJs": true, // Allow JavaScript files to be imported inside your project, instead of just .ts and .tsx files
"declaration": false, // Generate .d.ts files for every TypeScript or JavaScript file inside your project
"lib": ["es2019", "es2020", "es2021", "esnext"], // Specifies which default set of type definitions to use ("DOM", "ES6", etc)
"module": "commonjs", // Sets the module system for the program. "CommonJS" for node projects.
"noEmit": false, // Do not emit compiler output files like JavaScript source code, source-maps or declarations.
"outDir": "./dist", // .js (as well as .d.ts, .js.map, etc.) files will be emitted into this directory
"rootDir": "./", // The longest common path of all non-declaration input files
"sourceMap": true, // Enables the generation of sourcemap files
"target": "es2019", // Target environment
// Module resolution
"baseUrl": "./", // Sets a base directory to resolve non-absolute module names
"esModuleInterop": true, // fixes some issues TS originally had with the ES6 spec where TypeScript treats CommonJS/AMD/UMD modules similar to ES6 module
"moduleResolution": "node", // Pretty much always node for modern JS. Other option is "classic"
"paths": {
"#application/*": ["src/application/*"],
"#domain/*": ["src/domain/*"],
"#infrastructure/*": ["src/infrastructure/*"],
"#presentation/*": ["src/presentation/*"],
"#test/*": ["test/*"],
"#/*": ["src/*"]
}, // A series of entries which re-map imports to lookup locations relative to the baseUrl
"typeRoots": ["../node_modules/#types", "node_modules/#types", "src/types"],
// Advanced
"forceConsistentCasingInFileNames": true, // TypeScript will issue an error if a program tries to include a file by a casing different from the casing on disk
"listEmittedFiles": false, // Print names of generated files part of the compilation
"listFiles": false, // Print names of files part of the compilation
"resolveJsonModule": true, // Allows importing modules with a ‘.json’ extension, which is a common practice in node projects
"skipLibCheck": true, // Skip type checking of declaration files
"traceResolution": false, // Report module resolution log messages
// Experimental
"experimentalDecorators": true, // Enables experimental support for decorators
"emitDecoratorMetadata": true, // Enables experimental support for emitting type metadata for decorators which works with the module
// Strict checks
"strict": true, // Enable all strict type-checking options
// "allowUnreachableCode": false, // Pick up dead code paths
// "alwaysStrict": true, // Parse in strict mode and emit "use strict" for each source file
// "noImplicitAny": true, // Raise error on expressions and declarations with an implied 'any' type
// "noImplicitThis": true, // Raise error on 'this' expressions with an implied 'any' type
// "strictFunctionTypes": true, // Enable strict checking of function types
// "strictNullChecks": true, // Enable strict null checks
"strictPropertyInitialization": false, // Enable strict checking of property initialization in classes
// Linter Checks
"noFallthroughCasesInSwitch": true, // Report errors for fallthrough cases in switch statement
"noImplicitReturns": true, // Report error when not all code paths in function return a value
"noUnusedLocals": true, // Report errors on unused local variables
"noUnusedParameters": true, // Report errors on unused parameters
"pretty": true // Stylize errors and messages using color and context
},
"exclude": ["node_modules", "dist"], // Specifies an array of filenames or patterns that should be skipped when resolving include
"compileOnSave": false // Compile on save
}
tsconfig.build.json
{
"extends": "./tsconfig.json",
"compilerOptions": {
"sourceMap": false,
"inlineSourceMap": true,
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["src/**/*.test.ts", "src/**/*.spec.ts", "test/**/*.test.ts", "test/**/*.spec.ts", "node_modules", "dist"]
}
I already tried to build my entire application with webpack but it is a non-sense because webpack is used for frontend app.

Related

Compile with tsc run with node, ReferenceError: exports is not defined in ES module scope

This question has been asked many times before, but I can not find a solution
I'm using typescript in my project.
I have my index.ts file which contains code like this:
import { getEnvOptions } from './env/envreader';
import express, {Express, Request, Response} from 'express';
When I compile this with command tsc and try to run it with node
I get an error saying:
Object.defineProperty(exports, "__esModule", { value: true });
ReferenceError: exports is not defined in ES module scope
As I understand the problem is that the node runtime environment doesn't recognize the keyword "exports" that is generated by the typescript compiler. I intend to run this project on a server and not in a browser which is why I set the "module" as "CommonJS".
Here's the tsconfig:
{
"compilerOptions": {
"target": "ES5", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"module": "CommonJS", /* Specify what module code is generated. */
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */
"noEmit": false, /* Disable emitting files from a compilation. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"strict": true, /* Enable all strict type-checking options. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"include": ["./**/*.ts"],
}
What exactly should I do to make this run in a node environment if I want to keep using the typescript import statements?
Remove "type": "module" from package.json since you can use imports without specifying that in package.json, the reason is because import is part of typescript.

typescript 4.4.4: tsconfig paths not resolving as expected

my tsconfig.json
{
"compilerOptions": {
"target": "ES2018",
"module": "CommonJS",
"lib": ["ESNext", "ESNext.AsyncIterable"],
"skipLibCheck": true,
"sourceMap": true,
"outDir": "./dist",
"moduleResolution": "node",
"removeComments": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"resolveJsonModule": true,
"baseUrl": "./",
"paths": {
"#/*": ["src/*"],
"#config": ["src/config"],
}
},
"exclude": ["node_modules"],
"include": ["./src/**/*.ts"]
}
I am pretty sure there is nothing wrong in tsconfig paths because I even get path autocompletion from my IDE. however when my code is compiled (tsc --project tsconfig.json)
import { PRODUCTION } from '#config';
// or
import { PRODUCTION } from '#/config';
I get following javascript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const _config_1 = require("#config");
console.log(_config_1.PRODUCTION);
//# sourceMappingURL=index.js.map
but javascript doesn't understand what "#" is therefore I get "module not found" error. How can I solve this problem?
See this issue on TypeScript's GitHub project: Module path maps are not resolved in emitted code (#10866)
tl;dr
TypeScript won't rewrite your module paths
paths was designed to help TypeScript understand path aliases used by bundlers
You'll either need to:
use a bundler, such as webpack, and configure its own path maps
use a build time tool, such as tspath, to rewrite the paths
modify the runtime module resolver, with something like module-alias
I've been stuck with this issue for 2 whole days trying to find something that works. #Wing's answer is correct, if you're using plain tsc with no other tools, then Node won't know what you mean by #/ (or whatever your alias is) in your imports.
I've tried a few different approaches and I've found one that required no configuration.
What worked:
At the end of my journey I came across a package that is still actively being maintained: tsc-alias. The only thing I needed to do was add && tsc-alias -p tsconfig.json to my tsc command in my build script and voilà, it works. So your full command would be something like tsc -p tsconfig.json && tsc-alias -p tsconfig.json.
What I tried that didn't work:
I tried "module-alias" and I couldn't get it to work.
I tried "tsconfig-paths" as suggested by #AlexMorley-Finch with no luck as well. The package seems to be finding the modules but there seems to be a bug that causes the path of the modules not to be mapped correctly (there's an issue open for it).
I then came across tscpaths. This package requires you to explicitly specify your project, src dir and out dir. I got it to work after figuring out my outdir needed to be ./build/src instead of ./build. The caveat here: this package hasn't been touched since May 2019 as of writing this.

TypeScript won't compile mp3 audio file

Usecase
I am developing a Node JS CLI written in TypeScript. I need to play a notification sound on the successful completion of the CLI tasks. So, I've kept the audio file locally in a folder named assets.
Issue
I'm unable to compile the audio file along with the entire app in the final build. I'm using tsc -p . command which compiles all the files in the app except the audio file.
Folder Structure
Below is the folder structure of the app.
|_src
|_assets
|_notification.mp3
|_scripts
|_utils
|_templates
|_message
|_index.ts
TSConfig File
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"resolveJsonModule":true,
"declaration": false,
"outDir": "lib",
"downlevelIteration": true,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"exclude": ["./src/scripts/minify.ts", "my-app"]
}
Note: Please don't suggest using a file storage service as I need to make sure the tool works offline.

SyntaxError: Unexpected token import- Node.js

I am trying to code split my app with webpack but webpack is not creating the chunks for my dynamic imports. I found one source here:
https://davidea.st/articles/webpack-typescript-code-split-wont-work
which said I needed to change my module property in my tsconfig file from "commonjs" to "esnext" so the typescript compiler won't reduce my dynamic import statements to Promises, resulting in Webpack not knowing they are dynamic imports and thus not creating dynamic chunks. I followed this and during the compilation I can see the chunks now being created! Whoooo! However, the compiler bugs out, with the error in the title of my question, when trying to resolve the import statements in my express app as those ES imports are not being reduced to something node understands anymore. Does anyone know if this is possible to achieve? I want to use ES imports statements in node but without having the module property in my tsconfig file set to "commonjs". I really don't want to have to refactor all the imports statements to commonJS require statements.
tsconfig.json:
{
"compilerOptions": {
"target": "es6",
"module": "esnext",
"allowJs": true,
"sourceMap": false,
"inlineSourceMap": true,
"strict": true,
"strictNullChecks": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noEmitOnError": true,
"removeComments": false,
"forceConsistentCasingInFileNames": true,
"suppressImplicitAnyIndexErrors": false,
"jsx": "react",
"watch": false,
"moduleResolution": "node"
}}
node version: v8.11.3
I am using ts-node in my npm script to execute my server code:
"start": "webpack && ts-node -- ./src/service/index.ts --env=prod"
My .babelrc looks like the following:
{
"presets": [
"react",
"stage-3",
[
"env",
{
"modules": false
}
]
],
"plugins": [
"react-hot-loader/babel",
"syntax-dynamic-import"
]
}
By default, ts-node uses the compiler options in the tsconfig.json file in the working directory; you can specify a different tsconfig.json file with the --project option. The tsconfig.json file you use with ts-node must have module set to commonjs (or omitted, in which case ts-node defaults it to commonjs) in order for the on-the-fly compilation to generate modules that Node can understand. You may need to use separate tsconfig.json files for the server code you run with ts-node and the client code you package with Webpack.

WebStorm tsconfig.json appears to not update the correct files

I am developing a node.js application using TypeScript.
I've created a TypeScript file in the root folder of my project. I run tsconfig and it appears to update the dist folder. However, when I run the app, I am getting an error indicating a function is not defined.
Here is where things get confusing: there seems to be older versions of the .js and .map files in my src folder in the same directories as my source files with the same names. This .js file seems to have an older version of the file missing the necessary functions (class methods), different from the current versions in my /dist folder.
At the end of the day, I am trying to run the debugger on the files in my /dist folder and set breakpoints over in my /src TypeScript files.
This is a sample of the file structure I am seeing in my /src folder (this js file is not current):
Here is a sample of the file structure of my /dist folder where the transpiled js code resides:
Also, here are the debugger settings for the web app (rest) portion of the project:
Finally, here is a sample of the tsconfig.json file:
{
"compilerOptions": {
"module": "commonjs",
"target": "ES5",
"moduleResolution": "node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"types": ["reflect-metadata"],
"lib": ["ES6"],
"sourceMap": true,
"inlineSources": true,
"pretty": true,
"outDir": "./dist",
"rootDir": "./src/",
"noLib": false
},
"compileOnSave": true,
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"node_modules/#types"
]
}
I would like to understand what is wrong, causing it to read the wrong js files, instead of the ones in the /dist folder?
What can I do to fix this to point to the /dist folder. I thought the debugger settings I setup would do that, but this does not appear to be the case.
Update: I deleted the .js files that were generated in the src folder and they eventually returned back to the folder and once again, they were not current after making other changes. I am not sure what is generating these files; is it a setting in webstorm or is it in tsconig.json?
Something else doesn't look right. When I opened one of the files in the dist folder, I found the following code instead of JS code:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=group.js.map
This is not what I was expecting, as I was expecting to see the transpiled js code.
You are declaring src in your includes and in your rootDir, everything should be declared relative to rootDir, but in this case you likely don't need the includes or excludes since you're including everything in src anyway. The compileOnSave option is what is generating the files as you delete them because a watcher has been set up to do so. You're also mixing your target and lib javascript versions.
You do not need to explicitly exclude #types if you are using the types property already.
If types is specified, only packages listed will be included.
Here's a cleaned up config you can try
{
"compilerOptions": {
"module": "commonjs",
"target": "ES5",
"moduleResolution": "node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"types": ["reflect-metadata"],
"lib": ["ES5"],
"sourceMap": true,
"inlineSources": true,
"pretty": true,
"outDir": "dist",
"rootDir": "src",
"noLib": false
},
"compileOnSave": false
}

Resources