Cannot use import statement outside a module when run my typescript project

I am writing a "Hello world" RabbitMQ project in typescript. I am using Yarn as package management tool instead of NPM.

I have installed the RabbitMQ client library amqplib & its type definition. My package.json looks like this:

{
  "name": "rabbitmq-demo",
  "version": "1.0.0",
  "main": "src/publisher.ts",
  "license": "MIT",
 
  "dependencies": {
    "@types/amqplib": "^0.8.0",
    "amqplib": "^0.8.0"
  },
  "devDependencies": {
    "ts-node": "^10.4.0",
    "typescript": "^4.5.4",
  }
}

My tsconfig.json has following options enabled:

{
 "compilerOptions": {
   "target": "es2016", 
   "module": "commonjs",
   "rootDir": "./src",
   "outDir": "./dist",
   "esModuleInterop": true,
   "forceConsistentCasingInFileNames": true, 
   "strict": true,  
   "skipLibCheck": true
 }
}

My ./src/publishier.ts, you don't need to care about the logic in the code for my question, I just want to indicate I have this typescript file I wrote:

import * as amqp from "amqplib";

const msg = {number: 6};

const connect = async () => {
    try {
        const connection = await amqp.connect("amqp://localhost:5673");
        const channel = await connection.createChannel();
    
        const queue = await channel.assertQueue("jobs"); // create queue named "jobs"
        channel.sendToQueue("jobs", Buffer.from(JSON.stringify(msg)));
        console.log(`Job sent successfully! ${msg.number}`);


    } catch(e) {

    }
    
}

connect();

When I run node src/publisher.ts, I get error:

import * as amqp from "amqplib";
^^^^^^

SyntaxError: Cannot use import statement outside a module

Then I added "type": "module" in package.json & run again, but get new error:

node:internal/errors:464
    ErrorCaptureStackTrace(err);
    ^

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /rabbitmq-demo/src/publisher.ts

So, what is the root cause of the problems? How to run my publisher.ts?


Solution 1:

We are asking nodejs to execute a ts file, instead...

  1. Either we ask nodejs to execute the js file generated after building the source
  2. Or, we should ask ts-node to execute the ts file

We can try with following scripts in package.json...

{
  "name": "rabbitmq-demo"",
  "version": "1.0.0",
  "main": "dist/publisher.js",
  "license": "MIT",
  "devDependencies": {
    "ts-node": "^10.4.0",
    "typescript": "^4.5.4",
    "@types/amqplib": "^0.8.0",
    "amqplib": "^0.8.0"
  },
  "scripts": {
    "exec": "node dist/publisher.js",
    "preexec": "npm run build",
    "execwithargs": "node dist/publisher.js 'arg1' 'arg2' 'arg3'",
    "preexecwithargs": "npm run build"
    "build": "tsc",
    "start": "ts-node src/publisher.ts",
    "startwithargs": "ts-node src/publisher.ts 'arg1' 'arg2' 'arg3'"
  }
}

Having those scripts in package.json and assuming the outDir set in tsconfig.json is dist, we can execute by issuing any of the following on the terminal...

  1. npm run exec <--- This will execute via compile and run js file
  2. npm run exec 'arg1' 'arg2' 'arg3' <--- Run with command-line args
  3. npm run start <--- This will execute via ts-node using the ts file
  4. npm run start 'arg1' 'arg2' 'arg3' <--- Run with command-line args