Typescript adds plain export statement - node.js

I am building some typescript/react console app for Node.js.
If there is any import module in the source code it adds the export {}; into the output.
How I can get rid of the stuff please?
I use
typescript 4.1.2
ts-node
tsconfig:
{
"compilerOptions": {
"module": "ESNext",
"jsx": "react",
"esModuleInterop": true,
"moduleResolution": "node",
"skipLibCheck": true,
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
]
}
Source code:
import { useState, useEffect } from "react";
console.log("aaa");
Output:
console.log("aaa");
export {}; // <------ the problem
Fun fact: when I remove the import, the export disappear.

With the introduction of modules in ECMAScript 2015, ECMAScript has been split into two slightly incompatible languages: Scripts and Modules. However, there is no way to explicitly mark a resource as either a Script or a Module in-band. The only way to do that is out-of-band, e.g. via HTTP Content-Types or a command line flag that explicitly tells the ECMAScript engine to interpret the file as a Module.
So, in order to make it clear to the ECMAScript execution engine, that this is, in fact, a Module not a Script, the only way is to make sure that the resource can only legally interpreted as a Module and not a Script.
An empty export statement serves that purpose, since it is illegal in Scripts but has no side-effects.
Long story short: you cannot remove the export statement, because that will make the file ambiguous: it is impossible to tell from the source code alone whether the file is a Script or a Module. And for reasons of backwards-compatibility, most ECMAScript engines interpret ambiguous resources as Scripts. (A more strict engine might reject the file altogether, which is also not what you want.)
So, if you remove the export statement, your file will no longer be interpreted as a Module. However, the TypeScript source file is a Module (because it contains an import statement), therefore the compiler must emit the export statement in order to ensure that the compiled file is also a Module.

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";

How to use TypeScript import statement instead of <reference path...> in a Web application (ASP.NET Core)?

Context
I have (had) a working version typescript Hello World in my Web application (ASP.NET Core)
Using typscript compiler via NuGet package "Microsoft.TypeScript.MSBuild" Version="4.4.2" and tsconfig.json. (see below)
I've wanted to use a 3rd party lib, and successfully added "#types/lodash": "^4.14.175" via packages.json (see below)
I've added /// <reference path="../node_modules/#types/lodash/index.d.ts"/> (see below)
All works, but the line /// <reference path="..." is underlined green and ESLint says
Do not use triple slash reference for index.d.ts, use import instead.
OK, I am going to use export/import later anyway, so I've edited the triple slash reference line to be a comment, and added the line import * as _ from "lodash" which compiles fine, but when running in chrome causes runtime error:
Cannot use import statement outside a module
so I changed my <script tag to the following: <script type="module" src="~/js/app.js"></script>
However this causes the following chrome runtime error:
Failed to resolve module specifier "lodash". Relative references must start with either "/", "./", or "../".
Question
Now I am stuck, and with my very limited knowledge somehow I guess some step/transformation is missing, but what? I've tried to include some path in my .ts file's import statement (causing compile errors). Compile time I would like to use the working import referring to the #typings, but runtime the lodash.js is coming from cdn, the two nothing to do with each other...
app.ts
// commented out / <reference path="../node_modules/#types/lodash/index.d.ts"/>
import * as _ from "lodash"
console.log(_.camelCase("Hello"));
emitted app.js
// commented out / <reference path="../node_modules/#types/lodash/index.d.ts"/>
import * as _ from "lodash";
console.log(_.camelCase("Hello"));
//# sourceMappingURL=app.js.map
index.html
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.21/lodash.min.js"></script>
<script type="module" src="~/js/app.js"></script>
tsconfig.json
{
"compileOnSave": true,
"compilerOptions": {
"noImplicitAny": false,
"noEmitOnError": true,
"removeComments": false,
"sourceMap": true,
"target": "es6",
"module": "ES6",
"outDir": "wwwroot/js"
},
"exclude": [
"node_modules",
"wwwroot"
]
}
packages.json
{
"version": "1.0.0",
"name": "asp.net",
"private": true,
"devDependencies": {
"#types/lodash": "^4.14.175"
}
}
Try to modify the tsconfig.json
// tsconfig.json
{
...
#types: ["node_modules/"] // or typings
}
Or use ES5 require
const _ = require("lodash");
Found the following possible solutions
The required files need to be copied over to wwwroot folder, where they can be accessed when the application runs.
For this you'd need either use the bundler to bundle the files together (should be in default ASP.NET Core project template) or use task runners such as Gulp or Grunt to run tasks on build/publishing, which does that for you. See ASP.NET Core Docs on Gulp examples.
Original answer: https://stackoverflow.com/a/43513137/13747848
Note: Please give credit to original respondent!
Edit
For the error
Uncaught TypeError: Failed to resolve module specifier "lodash". Relative references must start with either "/", "./", or "../".
As of 2021, please consider the following statement by Márton Salomváry (Jan 2018):
Unfortunately even most libraries authored or published in ES6 module format will not work because they target transpilers and rely on the Node.js ecosystem. Why is that a problem? Using bare module paths like import _ from 'lodash' is currently invalid, browsers don’t know what to do with them.
And also the statement by Jake Archibald (May 2017):
"Bare" import specifiers aren't currently supported.
Valid module specifiers must match one of the following:
A full non-relative URL.
Starts with /.
Starts with ./.
Starts with ../.
And javascript.info:
In the browser, import must get either a relative or absolute URL. Modules without any path are called “bare” modules. Such modules are not allowed in import.
Certain environments, like Node.js or bundle tools allow bare modules, without any path, as they have their own ways for finding modules and hooks to fine-tune them. But browsers do not support bare modules yet.
Bundlers facilitate the use of "Bare Imports" which is not supported by the browser yet. Unless you bundle your code, I recommend using the solution proposed by #Asler. Besides, a lot of work is currently being done to study the implementation of "Bare Imports" in the browser, please follow this link if you want to monitor the overall progress.
Original answer: https://stackoverflow.com/a/66484496/13747848
Note: Please give credit to original respondent!
If you don't wish to use any bundling tools, you will need to provide a path to the lodash folder within node_modules, relative to the JavaScript file that you have the import statement in.
If you do not wish to use a bundler, it would also be worthwhile importing from the specific file, the function you need. For example:
import _each from '../node_modules/lodash/each'
Original answer: https://stackoverflow.com/a/52558858/13747848
Note: Please give credit to original respondent!

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

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

How to use node --experimental-modules with Typescript output

Might be the stupidest question ever, but I have a Node project that is using ES modules with --experimental-modules and Node 12.
Now I'm adding an inner package to the monorepo that's written in Typescript that's consumed by the main node app. I'm struggling with my Typescript build settings that produce something that will work with --experimental-modules.
Currently tsconfig.json:
{
"include": [
"src/**/*ts"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
],
"compilerOptions": {
"module": "esnext",
"esModuleInterop": true,
"target": "esnext",
"moduleResolution": "node",
"sourceMap": true,
"outDir": "dist"
},
"lib": ["es2015"]
}
If I have index.ts that imports from a neighbour ts file:
import { schema } from './schema'
The built import statements is without any .js and this makes my node app choke:
(node:78972) ExperimentalWarning: The ESM module loader is experimental.
internal/modules/esm/default_resolve.js:79
let url = moduleWrapResolve(specifier, parentURL);
^
Error: Cannot find module /Users/viktor/dev/projects/kb-frontend/packages/graph/dist/schema imported from /Users/viktor/dev/projects/kb-frontend/packages/graph/dist/index.js
The reason is that the import is without .js - patching that in my dist directory of the ts build it works.
I cannot simply change my ts module to commonjs since this will also change the way my main exports are working with my esm based main giving me other errors:
import { server as graphMiddleware } from '#kb-front/graph'
^^^^^^
SyntaxError: The requested module '#kb-front/graph' does not provide an export named 'server'
I would not want to add a lot of .default here and there since the entire setup I have is to avoid commonjs alltogether and just use js and typescript with esm. Hey, I want the new shiny stuff.
What am I missing?
Two issues on using native module support in browser with Typescript:
https://github.com/microsoft/TypeScript/issues/13422
https://github.com/microsoft/TypeScript/issues/16577
From them I got the hack to just import with .js extension. Super weird, but works for my case.

Typescript error Cannot find module 'typescript-Collections' when changing the module to "umd" or "amd" in tsconfig.json

I am using VS17 Enterprise. I can easily import "typescript-collections" (as instructed on this page) if I had set my module option to commonjs in my tsconfig.json.
However, as soon as I change it to "umd" or "amd", I get error (see the screenshot please) saying that Cannot find module 'typescript-collections' and therefore, the two variables queue and queue1 will be of type any. I have also attached my tsconfig.json.
Any help will be greatly appreciated.
As of today this problem with this package remains and my solution was to use es6 as my module
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": [],
"module":"es6"
},
"exclude": [
"test.ts",
"**/*.spec.ts"
]
}
and then import the classes using the notation below -
import * as Collections from 'typescript-collections/src/lib';
It seems there is an issue with this dependency packaging.
I fixed a similar problem by explicitly specifying the moduleResolution compiler option.
{
"compilerOptions":
{
"moduleResolution": "node"
}
}
Apparently the moduleResolution is set to "node" automatically, if module is set to "commonjs". The Typescript compiler seems to use another resolution strategy otherwise.
A post in a GitHub issue thread led me to this idea.

Resources