TypeSscript: partial merge and ES6 modules

Hi!

I have tried to find a suitable approach for this "problem" but was not able to (yet), so thought, that I could ask here. Any advise would be appreciated.

I have a project in PHPStorm, which has some TypeScript code structures like this:
At the moment I use tsconfig.json to merge all the .ts files to main.js and then use a file watcher to minify it using Terser. This works totally fine, but I would like to utilize ES6 modules and with that I am hitting a wall.

In the current setup, scripts that are in `Pages` directory are supposed to be modules, while the rest of the codebase is `Common`, that is it's needed to be loaded on every page. In order to reduce number of HTTP requests, I want all the files in `Common` to be merged into 1 file. Obviously, it's quite easy to do with tsconfig.json, but then I am unable to utilize the modules, I have to merge them as well.

I have tried the following:

1. Use the same setup, but set

"module": "None"

in tsconfig. TypeScript complains, that I can't use `outFile` in such case.

2. Tried not using `outFile` - it results in compilation of each script separately. And technically I can use that: use terser to merge all JS files in `Common` and that's it, but in that case I loose source maps, because it does not look possible to feed several source maps to Terser. Which obviously complicates debugging, which I do not want.

3. Tried having 2 tsconfig files: one for `Common` with no modules and with `outFile`, the other one for `Pages` files without `outFile` but with ES6 for `module`. But this does not work because tsconfig for `Common` does not list the modules from `Pages`, thus it fails on lines referencing them and since tsconfig for `Pages` does not refer any files from `Common`, the modules fail on helper function or global variables, because they do not see them.

 

I am pretty sure, that this is something basic and I am missing something obvious here, but perhaps someone can advise what would possible approaches to having `Common` merged into single JS file, and `Pages` compiled as separate ones, but so that they could "see" each other still?

Hopefully I've explained clearly enough, but if not - I am ready to clarify, of course.

Thanks in advance.

12 comments
Comment actions Permalink

Having 2 separate tsconfig.json files seems to be a right way to go... What kind of problems using this setup have you faced? Normally referencing files shouldn't be a problem, as all modules referenced from current file are implicitly included in config... A sample project illustrating the issue would be helpful

0
Comment actions Permalink

I thought so, too, but if I do this setup (collapsed some folders for less clutter):

with top-level tsconfig.json having this setup:

{
"compilerOptions": {
//Type checking
"strict": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"exactOptionalPropertyTypes": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": false,
//Compiler options
"module": "None",
"target": "ESNext",
"moduleResolution": "nodenext",
"sourceMap": true,
"sourceRoot": "/js/",
"removeComments": true,
"outDir": "./",
"outFile": "./main.js",
"explainFiles": true,
"listFiles": true,
//Other settings
"allowJs": false,
"checkJs": true,
"composite": true,
"noErrorTruncation": true,
},
//Include only .ts files in the same dir
"include": [
"Common/*.ts",
"Common/CustomElements/*/*.ts",
],
"exclude": [
"node_modules"
]
}

and the one for `Pages` having this:

{
"compilerOptions": {
//Type checking
"strict": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"exactOptionalPropertyTypes": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noPropertyAccessFromIndexSignature": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": false,
//Compiler options
"module": "ESNext",
"target": "ESNext",
"moduleResolution": "nodenext",
"sourceMap": true,
"sourceRoot": "/js/",
"removeComments": true,
"outDir": "./",
"explainFiles": true,
"listFiles": true,
//Other settings
"allowJs": false,
"checkJs": true,
"composite": true,
"noErrorTruncation": true,
},
//Include only .ts files in the same dir
"include": [
"./bictracker/*.ts",
"./fftracker/*.ts",
"./usercontrol/*.ts"
],
"exclude": [
"node_modules"
]
}

if I export, for example `password.ts` like this:

it highlights all functions from the `Common`, because it probably needs them as modules as well

While in my `Init.ts` import `password.ts` module is also highlighted:

even, though the path was autocompleted by IDE. It also highlights a couple of global variables as "unused".

Obviously, I would prefer not to have all these errors highlighted and just ignoring them does not feel right.

Am I doing something wrong in this setup?

0
Comment actions Permalink

>it highlights all functions from the `Common`, because it probably needs them as modules as well

did you try referencing them with the /// <reference path="..." />  comments (https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-path-)?

>While in my `Init.ts` import `password.ts` module is also highlighted:

did you try specifying a path in a different way? using the relative path, for example, not using leading slashes?

0
Comment actions Permalink

If I add reference path to files in `Pages` 

/// <reference path="../../main.d.ts" />

 they stop showing errors. Until I try to import, with which I can get, at best this:


So it suggests to include the files I am importing. Which would then try to merge them in the output, which I do not want to do. But even if I do include them, I can get to proper import statements like this:

And the catch is, that I want to use them in a different file (and dynamically). Ok, maybe I should use imports in the file, where I actually need them and not in "init" one. I move them to code likes this:

Which seems fine, but then I get lots of errors during compilation of the top-level tsconfig (the one for `Common`):

I presume this is happening because of the reference I used in `Pages` files

0
Comment actions Permalink

I definitely made a step forward, since I was able to resolve all of the above: instead of 

/// <reference path="../../main.d.ts" />

in each TS file in `Pages`, I updated tsconfig for `Pages` with 

"references": [
{"path": "../tsconfig.json"}
]

and for top-level tsconfig (for `Common`) I removed the `Pages` files from "include" and added

"references": [
{"path": "Pages/tsconfig.json"}
]

Now both tsconfig.json files compile without any errors and I can confirm, that the output file for `Common` does not include any of the modules. But... The file paths to the modules themselves is like

'../Pages/bictracker/keying.js'

and it is output to the JS the same way, resulting in web-browser trying to download exactly that (which transforms to https://local.simbiat.ru/Pages/bictracker/keying.js and the like), which does not exist. Using different combination with and without slashes, with and without "./" or "../", but no luck so far. And the thing is, IDE finds the files by exactly this kind of string.

0
Comment actions Permalink

>Until I try to import, with which I can get, at best this:

the error is "TS1005: 'from' expected.", this is a syntax error as you are using the wrong 'import' syntax. If you meant to import the entire module, use import * as <alias> from 'path' (https://www.typescriptlang.org/docs/handbook/modules.html#import-the-entire-module-into-a-single-variable-and-use-it-to-access-the-module-exports)

 

0
Comment actions Permalink

Yes, I figured that one out while I was writing the previous post. Although, I'll be honest the error itself is not exactly obvious. 😅

0
Comment actions Permalink

As for import paths: replacing them with

'/js/Pages/bictracker/keying.js'

helps after compilation, since then URLs are proper, but TypeScript itself does not like the paths

I can ts-ignore those, of course, does not seem to affect anything else, but I would prefer to figure paths, that are proper both for the compiler and the browser.

0
Comment actions Permalink

So, based on documentation and considering the structure that I have it would looks as if

"moduleResolution": "classic",

would make sense, but, if I set it like that (instead of "nodenext"), PHPStorm finds appropriate types, but yet highlights the import:

So, I returned "nodenext" as I had it previously and added

"baseUrl": ".",
"paths": {
"*": ["*", "../*"],
},

Which solves the issue, but feels very counterintuitive, but could be specifics of TypeScript with dynamic imports? Although all the posts I've found about dynamic imports do not even add ".js" at the end of the modules' paths.

Unfortunately there is still 1 piece of the puzzle, that needs solving: if you go, for example, to https://www.simbiat.ru/bictracker/keying/ where I have deployed the modules already, for some reason the mapping to TS file related to keying.js does not load, because it seems to be referencing my test (local) server. And it also lacks "Pages" in the path.

And I do not see https://local.simbiat.ru/js/bictracker/keying.ts referenced anywhere.

Changing 

"sourceRoot": "/js/",

to different values (js, /js/Pages/, js/Pages, .etc) does not seem to affect anything.

0
Comment actions Permalink

Nevermind the issue with sourcemaps: looks like this is an issue in MS Edge specifically, because in Chrome and Firefox they are recognized and shown correctly.

Aside from that: I wonder if the result of this thread can be used to implement IDE's suggestions of some sort? Since, as I mentioned, the solution was not exactly intuitive. Perhaps check if the path for dynamic import corresponds to projects web address and suggest respective path mapping?

0
Comment actions Permalink

>Perhaps check if the path for dynamic import corresponds to projects web address and suggest respective path mapping?

no, I don't think that such heuristic approach would be helpful, all this is very specific to the project setup

0

Please sign in to leave a comment.