How to generate d.ts and d.ts.map files using webpack? - javascript

I can't find a way to generate d.ts and d.ts.map files using webpack. babel-loader only generates js and js.map files. I also need d.ts and d.ts.map files (which I was able to generate using tsc command) as shown in this picture:
Here is a minimal repo that contains all the settings: https://github.com/stavalfi/lerna-yarn-workspaces-example
More Details
I moved to Lerna + yarn. One of my packages is core (will be used in other packages) which is written in TS and JS.
I'm using webpack 4,babel-loader 8 for ts-to-js.
The other packages are trying to find type definitions and implementation of my core package but I was only able to generate index.js and index.js.map with webpack:
output: {
path: distPath,
filename: 'index.js',
},
{
"extends": "../tsconfig.settings.json",
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"declarationDir": "dist",
"rootDir": "src",
"outDir": "dist"
}
}
When I compile with tsc (without webpack), everything is working great as I showed in the picture above.
Does my strategy is wrong? what should I do?
I have tried a lot of plugins that generate d.ts files but they don't work and doesn't create d.ts.map files.
I already tried: typescript-declaration-webpack-plugin, npm-dts-webpack-plugin, dts-bundle-webpack, #ahrakio/witty-webpack-declaration-files. (They are listed in the package.json of core so you can clone and play with it).

Running ts-loader before babel-loader will do the trick.
Specifying that you want declaration files in config is all you need.
If you are using an absolute path, the output d.ts files will also contain absolute paths which are useless and will result in typescript compilation errors.
To fix that, I wrote a plugin to convert an absolute path to a relative path:
https://github.com/stavalfi/babel-plugin-module-resolver-loader

You can call the Typescript compiler tsc directly to do that.
Use tsc --declaration to output a .d.ts file and tsc --declarationMap to generate the corresponding map file.
You can find more documentation here:
https://www.typescriptlang.org/docs/handbook/compiler-options.html

Related

Eslint/Tslint Config help (excluding files)

Recently in our project we migrated to use the #typescript-eslint/parser to slowly migrate from tslint. Everything has mostly been smooth but I have a small problem/question I can't work out.
Do I need to specify ignore files/patterns in both the tsconfig exclude array, as well as the ignorePatterns on the .eslintrc export object? What is the difference?
We have a messages.js file inside our src/lang directory that I'm trying to ignore but currently throws a lint error on our pre-commit hook, which got me wondering about this question and how these two setups work together.
Parsing error: "parserOptions.project" has been set for '#typescript-eslint/parser'
The file does not match your project config: src/lang/messages.js. The file must be included in at least one of the projects provided
I think my understanding of these intertwine is off, as when eslint runs, I thought the parserOptions would pick up the project rules from the tsconfig, where the js files are excluded.
Currently, the sections I'm talking about in our eslintrc looks like:
module.exports = {
parser: '#typescript-eslint/parser',
parserOptions: {
project: path.resolve(__dirname, './tsconfig.json'),
tsconfigRootDir: __dirname,
useJSXTextNode: true,
sourceType: 'module',
ecmaFeatures: {
modules: true,
jsx: true,
},
},
ignorePatterns: ['node_modules/**', '.storybook/**', 'src/stories/**', '*.scss', '*.js'] // ignoring here works
tsconfig:
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules", "src/**/*.js"], // exclude here doesn't work.
package.json:
"scripts": {
"lint": "tsc --project ./tsconfig.json --noEmit && eslint --ext=jsx,ts,tsx src"
},
​
ESLint passes the #typescript-eslint/parser plugin a list of files (which ESLint obtains from the command-line). The #typescript-eslint/parser plugin cannot control this list, so it does its best to act based upon the list it is given.
The error message is attempting to inform the user that ESlint is linting a file that is excluded from being parsed by the #typescript-eslint/parser plugin. From the plugin's perspective, there are two reasons this could occur:
The file was purposefully, explicitly excluded (by adding it to the tconfig exclude).
The user forgot to add the file to the tsconfig include.
In the latter case (which is much more common), the plugin wants to inform the user that their config is wrong, so that they can correct it. Hence the error message you saw.
In order to correctly exclude files from TSLint, one option is to use a .eslintignore file.
You can also change the eslint command to ignore the excluded files:
eslint ... --ignore-pattern "src/**/*.js"
(But be aware that the ignore pattern is relative to the current directory, not relative to the location of tsconfig etc.)
Alternatively, if you do want ESLint to lint the files (but still exclude them in tsconfig), you can consider providing a more inclusive tsconfig for #typescript-eslint/parser by creating a tsconfig.eslint.json file that extends from your normal tsconfig.
Also see these GitHub issues:
#typescript-eslint/parser doesn't ignore files, which is excluded by/in tsconfig.json #905
parse error on excluded files #1174
Exclude files in tsconfig without throwing an #typescript-eslint/parser error #1350
I was able to use the tsconfig exclude by using the JS config file .eslintrc.js and doing the following:
const tsConfig = require('./tsconfig.eslint.json');
module.exports = {
...
ignorePatterns: tsConfig.exclude,
};

Webpack using Typescript: Common settings in both configs, so which take precedence?

I am in the initial stages of converting a javascript (with webpack) project into a typescript project and one of the many confusions I have is that their appears to be some configuration settings that can appear in webpack and typescript, so which takes precedence?
(I'm currently working on a node cli application).
Eg, the primary example is which files are included in compilation.
In webpack config, you can specify which input files are selected with rules:
module: {
rules: [
{
test: /index.ts/,
use: 'shebang-loader'
},
{
test: /\.ts(x?)$/,
use: 'ts-loader'
},
{
test: /\.json$/,
use: 'json-loader'
}
]
},
As you can see from above, I'm using various loaders for different file types.
Then my initial tsconfig.json is as follows:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"noImplicitAny": true,
"lib": [
"es5", "es2015", "es6", "dom"
]
},
"include": [
"lib/**/*"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}
So what is the true nature of the interaction between webpack and typescript? I have not been able to discern this from typescript or webpack documentation. Both appear to be able to specify the input files that are included in compilation.
Another (probably better example) is the output file. In webpack config I have the following:
output: {
libraryTarget: 'commonjs',
path: path.join(__dirname, 'dist'),
filename: 'application-bundle.js'
}
which defines 'application-bundle.js' is the output file inside the 'dist' folder.
In the tsconfig, you can have something like the following:
"outFile": "./dist/application-bundle.js",
(I currently do not define an outFile property, but I know you can do so, but in being able to do so, brings up ambiguities and hence confusion). So in this scenario, does webpack override typescript or vice-versa? What is the recommended strategy?
There are probably more typescript/webpack crossovers that are going to cause confusion, but the 2 that I have described so far are the most upfront and pressing issues I need to understand, thanks.
EDIT: Having thought about it, I also need another clarification. I am guessing when you build a TS/WP project, the TS compiler runs first creates all the .js files (let's say in the .dist folder). Then webpack bundles all the generated .js files. So assuming this is correct, do I then need to configure webpack to use as its input the .js files in the dist folder instead of the .js files inside the project source (ie everything under ./lib which is where my source js files are)?
I was hoping to convert my project in an incremental manner so what I have just said does not really fit my needs. Webpack would need to pick up some .js files from ./dist and some files from ./lib which have not yet been converted to typescript. I don't know how to modify the project for incremental upgrade.
[...] there appears to be some configuration settings that can appear in webpack and typescript, so which takes precedence?
You are right on the point that there is some natural redundancy in the configuration when Webpack is combined with TypeScript. Lets pick up your first question:
which files are included in compilation? [...] Another example is the output file. [...] So in this scenario, does webpack override typescript or vice-versa? What is the recommended strategy?
Simply spoken, both TypeScript and Webpack process/transform input files and emit the output in a target directory structure or file bundle(s) - both need some input/output configuration info. TypeScript compiler can run on its own, and also be integrated as file processor as part of a bigger Webpack build. With outFile option TypeScript can even be seen as a mini-bundler on its own, as it bundles all .ts-files to a single target file.
To answer the question, if TypeScript or Webpack configuration takes precedence, it is important to understand, how Webpack works. I'll quote one of your assumptions here:
I am guessing when you build a TS/WP project, the TS compiler runs first creates all the .js files (let's say in the .dist folder). Then webpack bundles all the generated .js files.
That is not quite correct. But good you said that, as it sheds more light on your core understanding problem. Everything in Webpack starts with the entry point you specify in the config - if you will, that's the input. From this entry module, webpack considers all other transitively imported modules (e.g. via import or require) and creates a dependency tree.
Every file in the dependency tree can optionally be transformed by Loaders, which can also be chained like a file processing pipeline. So ts-loader and the underlying TypeScript compiler apply transformations for .ts/.tsx files with test: /\.ts(x?)$/ predicate.
All in all you see, that Webpack considers your entry file, which leads to a bunch of further imported .ts/.tsx files (amongst your other file types, we neglect them here). And for each single file, the TypeScript loader will be invoked in the course of the loader processing pipeline. Therefore, it is inherent that TypeScript I/O config will be ignored and Webpack's entry/output configuration takes precedence in the build. All other TypeScript related settings are taken from tsconfig.json, as described in the ts-loader docs: "The tsconfig.json file controls TypeScript-related options so that your IDE, the tsc command, and this loader all share the same options."
I was hoping to convert my project in an incremental manner
It is perfectly fine to migrate from JavaScript to TypeScript in a stepwise manner!
Hope, that helps.

Does ts-node support '#' style import? If so, how to set it up?

I'm creating a command-line script, using classes from the main express app.
Script resides in the folder:
bin/utils/
├── sync-buyers.ts
└── tsconfig.json
The main express app is in /app use uses import '#/foo/bar/thing.
This is set up in the tsconfig.json of the main app, as follows:
"paths": {
"#/*": ["*"],
"*": [
"node_modules/*",
"app/typings/*"
]
}
},
"include": ["app/**/*", "test/**/*"],
"exclude": ["app/**/*.test.ts", "/__tests__/", "/__mocks__/", "/__snapshots__/", "app/**/__mocks__/"],
"files": ["typings/global.d.ts"]
Script Execution
I'm testing to see if I can import from the main app, so I created a sayHello() function.
#!/usr/bin/env ts-node
/* tslint:disable */
import { sayHello } from '../../app/services/v2/oapp';
sayHello();
When I run it:
TSError: ⨯ Unable to compile TypeScript:
../../app/services/v2/oapp.ts(9,19): error TS2307: Cannot find module
'#/helpers/fetch'.
../../app/services/v2/oapp.ts(10,31): error TS2307: Cannot find module
'#/services/v2/buyer'.
../../app/services/v2/oapp.ts(11,51): error TS2307: Cannot find module
'#/http/HttpHeader'.
Summary:
Does ts-node support '#' style of import? If so, how do I set it up?
So the TypeScript paths configuration only applies to TypeScript's type resolution and checking, meaning that it will allow TypeScript to understand those imports for the purposes of type-checking only, but the code it generates won't automatically rewrite those imports to the correct locations.
There's two common approaches for solving this:
Update the Node resolver to understand the TypeScript paths config. The generated files will still refer to those paths by their #-name.
Most commonly, the tsconfig-paths module is used for this. You can require that module from the node command directly:
node -r tsconfig-paths/register main.js
Rewrite the generated files so that the #-names get replaced with the "real" local relative path locations.
There's a standalone module for this, tspath - you simply run tspath after compiling your TypeScript, and it updates the generated files with the correct paths.
If you're using Webpack, you can also use tsconfig-paths-webpack-plugin, which will take care of configuring Webpack's resolver to correctly locate those #-name paths.
And finally if you're using Babel, you might be interested in babel-plugin-module-resolver which does a similar thing for the Babel toolchain, however the downside here is it doesn't read the paths config from tsconfig.json, so you essentially have to duplicate your paths config in the alias config of this plugin.
Personally I'd recommend tsconfig-paths if this is a Node script or server that's compiled with tsc directly and tsconfig-paths-webpack-plugin if this is a frontend Webpack build.

Why ngc partially ignore compileroptions.outDir? Does a workaround exist?

I have a tsconfig.json which specifies an outDir. The reason is that I want to separate the generated JavaScript output from the TypeScript sources.
So:
"compilerOptions": {
...
"outDir": "target/",
...
}
This works very well, until I compile the project with the typescript compiler only. All generated javascript output is created in the target/ directory.
But, if I call it with the angular compiler (ngc, it is essentially a wrapper around the tsc typescript compiler), we have an additional build step. It compiles the template files and components into typescript, which will be compiled further to javascript by the tsc.
These intermediary typescript files have the *.ngfactory.ts or *.ngsummary.json extension.
Now my problem is, that the ngc command generates these files still in my src/ directory, totally ignoring my outDir setting in my tsconfig.json.
What is the cause of this problem? Does any useful workaround exist?
Extension: regarding comments, ng from the angular-cli can do this. This leads to a side-question, how does it do with the ngc?
The cause of the problem was that ngc has some additional options in tsconfig.json what I didn't add.
The following settings in tsconfig.json do what I want.
"angularCompilerOptions": {
"genDir": "aot",
"skipMetadataEmit" : true
}

Exclude folder when compiling typescript

I use Atom to write code. It uses tsconfig.json to include and exclude folders. In order to use intellisense I need node_modules to be included, but when I want to compile it to js I don't want node_modules to be compiled.
So I need to call tsc in the upper folder where the config.ts is, and this results in compiling the whole node_modules.
My folder structure looks like this:
node_modules
config.ts
spec
|--test1.ts
|--test2.ts
Any idea how to exclude node_modules when compiling with tsc command?
Use exclude property
{
"compilerOptions": {
...
}
"exclude": [
"node_modules"
]
}
Files included using "include" can be filtered using the "exclude" property. However, files included explicitly using the "files" property are always included regardless of "exclude". The "exclude" property defaults to excluding the node_modules, bower_components, jspm_packages and directories when not specified.
link updated:
https://www.typescriptlang.org/tsconfig#exclude

Categories

Resources