I am trying to build a node app using typescript. The plan is to deploy the build
folder independently (i.e. it has it's own node_modules
). Here is an MWE of my folder structure:
root
|-node_modules
|-src
| |-index.ts
| |-other.ts
|-build
| |-node_modules
| |-index.js
| |-other.js
| |-package.json
| |-package-lock.json
|-package.json
|-package-lock.json
|-tsconfig.json
The index.ts
and other.ts
files contains the following:
//index.ts || //other.ts
import { HelloWorld } from "./other"; || export function HelloWorld() {
|| console.log("Hello World!");
HelloWorld(); || }
The package.json
file looks like (the real one contains devDependencies
and dependencies
as well):
{
"name":"MyApp",
"version":"0.1.0",
"type":"module"
}
The tsconfig.json
file looks like:
{
"pilerOptions": {
"outDir": "./build",
"strict": true,
"baseUrl":"src",
"rootDir":"./src",
"esModuleInterop": true
},
"include": [
"src"
]
}
I am building my build
folder by first copying the package.json
file into build
, running npm install --prefix ./build --omit=dev
to get a production ready node_modules
folder, then running npx tsc
to build the .js
files.
When I try and run node build/index.js
, I get the following error:
ERR_MODULE_NOT_FOUND:
Cannot find module 'C:Users\MichaelBarrowman\TestRepo\build\other' imported from 'C:Users\MichaelBarrowman\TestRepo\build\index.js'
I tried to change the moduleResolution
to node16
in the tsconfig.json
file, and I get:
Relative import paths need explicit file extensions in EcmaScript imports when '--moduleResolution' is 'node16' or 'nodenext'.
I was under the impression that using the Node16 resolution algorithm, the "./other"
in the import
statement in index.ts
/index.js
would be resolved to "./other.js"
automatically.
I am running locally on Node v18.12.1
(but these errors persist on a server using Node20
) with typescript
version 8.19.2
I am trying to build a node app using typescript. The plan is to deploy the build
folder independently (i.e. it has it's own node_modules
). Here is an MWE of my folder structure:
root
|-node_modules
|-src
| |-index.ts
| |-other.ts
|-build
| |-node_modules
| |-index.js
| |-other.js
| |-package.json
| |-package-lock.json
|-package.json
|-package-lock.json
|-tsconfig.json
The index.ts
and other.ts
files contains the following:
//index.ts || //other.ts
import { HelloWorld } from "./other"; || export function HelloWorld() {
|| console.log("Hello World!");
HelloWorld(); || }
The package.json
file looks like (the real one contains devDependencies
and dependencies
as well):
{
"name":"MyApp",
"version":"0.1.0",
"type":"module"
}
The tsconfig.json
file looks like:
{
"pilerOptions": {
"outDir": "./build",
"strict": true,
"baseUrl":"src",
"rootDir":"./src",
"esModuleInterop": true
},
"include": [
"src"
]
}
I am building my build
folder by first copying the package.json
file into build
, running npm install --prefix ./build --omit=dev
to get a production ready node_modules
folder, then running npx tsc
to build the .js
files.
When I try and run node build/index.js
, I get the following error:
ERR_MODULE_NOT_FOUND:
Cannot find module 'C:Users\MichaelBarrowman\TestRepo\build\other' imported from 'C:Users\MichaelBarrowman\TestRepo\build\index.js'
I tried to change the moduleResolution
to node16
in the tsconfig.json
file, and I get:
Relative import paths need explicit file extensions in EcmaScript imports when '--moduleResolution' is 'node16' or 'nodenext'.
I was under the impression that using the Node16 resolution algorithm, the "./other"
in the import
statement in index.ts
/index.js
would be resolved to "./other.js"
automatically.
I am running locally on Node v18.12.1
(but these errors persist on a server using Node20
) with typescript
version 8.19.2
-
1
"I am building my build folder by first copying the package.json file into build [...]" why?
tsc
already copies the piled source to the dir you tell it to, and the top levelnode_modules
will resolve just fine from any subdirectory; you don't need to copy your package.json and then runnpm install
insidebuild
. Where is the npm script that runs the typescript pile pass? – Mike 'Pomax' Kamermans Commented Oct 10, 2023 at 19:04 -
I am creating a separate
node_modules
directory in thebuild
folder because this folder is going to be deployed on it's own. The workflow is going to be under CI/CD pipeline and thebuild
folder will be a separate branch. – Michael Barrowman Commented Oct 11, 2023 at 8:11 - Can't say that makes a lot of sense, you may want to read up on how TS-based projects are typically deployed a bit (you're basically doing something very unusual probably for reasons that don't actually require you to do this at all) – Mike 'Pomax' Kamermans Commented Oct 11, 2023 at 15:28
- I appreciate the concern for my workflow, but your response doesn't really address the question I was asking. – Michael Barrowman Commented Oct 20, 2023 at 9:33
- 1 That's why it was a ment, not an answer post. Not all problems are best solved by posting an answer, some require reexamining the setup that allows the problem to even exists. (And when you do something very unusual, you typically want to explain why in your post, so that folks who are familiar with the thing you're asking about and the methodologies around that can understand why you're doing something so different from convention, because otherwise the first step is to stop doing things differently and follow the normal approach and see if that fixes things) – Mike 'Pomax' Kamermans Commented Oct 20, 2023 at 16:54
1 Answer
Reset to default 8When you specify your package / project with "type": "module"
in package.json
, your relative file imports MUST contain the file extension.
relative import paths need full extensions (e.g we have to write
import "./foo.js"
instead ofimport "./foo"
)
Furthermore, even if you initially write your file in TypeScript (with .ts
file extension), you should directly use the final file extension (so .js
); tsc
knows how to handle it.
it will have to be rewritten to use the extension of the output of
foo.ts
- sobar.ts
will instead have to import from./foo.js
.
So simply make sure to add file extensions to your imports:
import { HelloWorld } from "./other.js"; // Notice the file extension