My tsconfig.json cannot find a module in my node_modules directory, not sure what is wrong - node.js

I have the following hierarchy:
dist/
|- BuildTasks/
|- CustomTask/
- CustomTask.js
node_modules/
source/
|- BuildTasks/
|- CustomTask/
- CustomTask.ts
- tsconfig.json
Additionally, I am trying to create a VSTS Task extension for internal (private) usage. Originally, I had my tsconfig.json at my root directory, and everything worked just fine on my local machine. The problem is that a VSTS Extension requires all the files to be included in the same directory as the task folder itself. See https://github.com/Microsoft/vsts-task-lib/issues/274 for more information:
you need to publish a self contained task folder. the agent doesnt run
npm install to restore your dependencies.
Originally, I had a this problem solved by include a step to copy the entire node_modules directory into each Task folder, in this case my CustomTask folder which contains my JS file. But, this seems a bit much considering that not every task I am writing has the same module requirements.
My idea was to create a tsconfig.json in each of the Task folders which would specify to create a single output file containing all of the dependent modules, but unfortunately it is not working:
{
"compilerOptions": {
"baseUrl": ".",
"target": "ES6",
"module": "system",
"strict": true,
"rootDir": ".",
"outFile": "../../../dist/BuildTasks/CustomTask/CustomTask.js",
"paths": {
"*" : ["../../../node_modules/*"]
}
}
}
Prior to adding the "paths", I was getting the following errors:
error TS2307: Cannot find module 'vsts-task-lib/task'.
error TS2307: Cannot find module 'moment'.
After adding the paths, I still get the error that it cannot find the module 'moment', which is in my node_modules directory. Also, when I look at the output JS it seems that it didn't include the 'vsts-tasks-lib' code necessary, maybe because it still had an error in regards to the 'moment' module? Not sure what I missed?

Using webpack to compile JavaScript modules, simple sample:
webpack.config.js:
const path = require('path');
module.exports = {
entry: './testtask.ts',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
node: {
fs: 'empty'
},
target: 'node'
};
After that, there are just bundle.js and task.json in task folder.
Update: sample code in testtask.ts:
import tl = require('vsts-task-lib/task');
import fs = require('fs');
console.log('Set variable================');
tl.setVariable('varCode1', 'code1');
tl.setTaskVariable('varTaskCode1', 'taskCode1');
var taskVariables = tl.getVariables();
console.log("variables are:");
for (var taskVariable of taskVariables) {
console.log(taskVariable.name);
console.log(taskVariable.value);
}
console.log('##vso[task.setvariable variable=LogCode1;]LogCode1');
console.log('end========================');
console.log('current path is:' + __dirname);
fs.appendFile('TextFile1.txt', 'data to append', function (err) {
if (err) throw err;
console.log('Saved!');
});
console.log('configure file path:' + process.env.myconfig);
console.log('configure file path2:' + process.env.myconfig2);

Related

Bundle NPM Package so it has different import paths with Vite and Typescript

How can I bundle my NPM package in a way that I can have different import paths for different parts of the package? I have found webpack approaches, but I am using Vite and TS.
My package looks like this:
- src
- atoms
- molecules
- organism
- index.ts (currently simply imports and exports everything)
Now I can use this currently like this
import { Button } from '#mypackage/library'
How can I do it, so I get this outcome:
import { Button } from '#mypackage/library/atom'
Here is the relevant part of my package.json
{
"entry": "src/index.ts",
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
"types": "dist/index.d.ts",
"files": [
"dist",
"src"
],
"exports": {
".": {
"import": "./dist/index.es.js",
"require": "./dist/index.cjs.js",
"types": "./dist/index.d.ts"
},
"./package.json": "./package.json",
"./atoms": "./src/atoms/index.ts",
"./molecules": "./src/molecules/index.ts",
"./organisms": "./src/organisms/index.ts",
"./theme": "./src/theme/index.ts"
},
}
Here is my vite.config.ts
export default defineConfig({
build: {
lib: {
entry: path.resolve(__dirname, 'src/index.ts'),
formats: ['es', 'cjs'],
name: '#workdigtital/component-library-react',
fileName: (format) => `index.${format}.js`
},
rollupOptions: {
external: ['react', 'react-dom'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM'
},
exports: 'named'
}
}
},
plugins: [react(), dts({ insertTypesEntry: true })],
resolve: {
alias: {
'#': path.resolve(__dirname, './src')
}
}
});
If I currently try an import like this, inside another project (Laravel+React), in which installed the library.
import { ThemeProvider } from '#workdigital/component-library-react/theme';
I get the following run time error (But no Typescript errors, even IntelliSense is working):
Failed to load url /resources/js/theme/ThemeProvider (resolved id: /resources/js/theme/ThemeProvider). Does the file exist?
My resulting Dist folder looks like this:
You can't have TypeScript exports, this simply won't work. An npm package should have only JS exports.
If you want to be able to selectively import different parts of your package, you must transpile them to different files.
rollup can do it, but it is lots of work, as you will have to set up a separate target for each exported file. Normally you use rollup to create a single bundle, this what this tool is made for.
tsc with a tsconfig.json will be a much better choice in your case. It does this by default, you only need to specify the output directory and it will produce a separate file for each source.
There is an excellent guide on the TypeScript site about packaging TypeScript libraries, you should probably start there.

jest cannot resolve module aliases

I am using a npm module called module-alias. I map some modules in tsconfig.json and package.json
tsconfig.json
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"#config/*": ["config/*"],
"#interfaces/*": ["interfaces/*"],
"#services/*": ["services/*"]
},
"module": "commonjs",
"target": "es2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"sourceMap": true,
"outDir": "./dist", /* Redirect output structure to the directory. */
"rootDir": "./src",
"allowSyntheticDefaultImports": true /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
}
package.json
...
"_moduleAliases": {
"#config": "src/config",
"#interface": "src/interface",
"#services": "src/services"
},
"jest": {
"moduleNameMapper": {
"#config/(.*)": "src/config/$1",
"#interface/(.*)": "src/interface/$1",
"#services/(.*)": "src/services/$1"
},
"moduleFileExtensions": ['js', 'json', 'jsx', 'ts', 'tsx', 'node']
},
...
server.ts
import { logger } from '#config/logger';
everything works fine when I run npm start, but it gives me an error when I run jest
FAIL src/test/article.spec.ts
● Test suite failed to run
Cannot find module '#config/logger' from 'server.ts'
However, Jest was able to find:
'rest/server.ts'
You might want to include a file extension in your import, or update your 'moduleFileExtensions', which is currently ['js', 'json', 'jsx', 'ts', 'tsx', 'node'].
Anyone know what the problem is? thanks
Solution works for me (Update 18/10/2019) :
Create a jest.config.js with code below:
module.exports = {
"roots": [
"<rootDir>/src/"
],
"transform": {
"^.+\\.tsx?$": "ts-jest"
}
}
and update moduleNameMapper in package.json:
...
"_moduleAliases": {
"#config": "./src/config",
"#interfaces": "./src/interfaces",
"#services": "./src/services"
},
"jest": {
"moduleNameMapper": {
"#config/(.*)": "<rootDir>/src/config/$1",
"#interfaces/(.*)": "<rootDir>/src/interfaces/$1",
"#services/(.*)": "<rootDir>/src/services/$1"
}
}
...
After a few hours I've managed to make this work. I'll try my best to simplify it to save others time and make it smooth.
My app is of the following stack:
Typescript (tsc and ts-node/register, no Babel) for the API build (.ts)
React (.tsx using Webpack)
jest for API testing (no Babel)
testcafe for UI/E2E
VSCode as IDE
Key notes:
The solution that works for me is must have "#" character in front of the alias. It's not required in theory by module-alias, however Jest is getting lost when we apply the module name mapper.
There needs to be consistency for naming between 'webpack' aliases and 'tsconfig'. It is necessary for VSCode not to red underline module names in TSX files.
This should work regardless of your document structure but remember to adapt baseUrl and jest config if encounter issues.
When applying changes in VSCode for .tsx files do not be worried that some of the paths are underlined. It's temporary as VSCode seems to grasp it only when all files are correctly connected to each other. It demotivated me at the start.
First, install module-alias from https://www.npmjs.com/package/module-alias with
npm i --save module-alias
then add to your initial startup file (for .ts files only, i.e. your application server):
require('module-alias/register')
as the module-alias docs indicate. Then setup tsconfig.ts. Keep it mind that baseUrl is relevant here as well:
{
"compilerOptions": {
"baseUrl": ".",
...
"paths": {
"#helpers/*": ["src/helpers/*"],
"#root/*": ["src/*"]
...
}
}
then setup your webpack.js:
const path = require('path')
...
{
resolve: {
...
alias: {
'#helpers': path.resolve(__dirname, 'src', 'helpers'),
'#root': path.resolve(__dirname, 'src')
}
}
}
then setup your package.json:
{
...
"_moduleAliases": {
"#helpers": "src/helpers",
"#root": "src"
}
}
then setup your jest config (I attach only things that were relevant when applying my change):
{
rootDir: ".",
roots: ["./src"],
transform: {
"^.+\\.ts?$": "ts-jest"
},
moduleNameMapper: {
"#helpers/(.*)": "<rootDir>/src/helpers/$1",
"#root/(.*)": "<rootDir>/src/$1"
},
...
}
Now, we need to take care of the build process because tsc is not capable of transpiling aliases to their relative siblings.
To do that we will use tscpaths package: https://github.com/joonhocho/tscpaths . This one is simple.
So considering your build command was just:
tsc
Now it becomes
tsc && tscpaths -p tsconfig.json -s ./src -o ./dist/server
You need to adjust your -s and -o to your structure, but when you inspect your .js file after build you should see if the relative path is correctly linked (and debug accordingly).
That's it. It should work as ace. It's a lot but it's worth it.
Example of a call in the controller (.ts) and in React component (.tsx) file:
import { IApiResponse } from '#intf/IApi'
In my case to support #Greg Wozniak's answer, I only needed to fix my jest config file.
My app uses jest.config.ts
In my src/index.ts I have import "module-alias/register"
In package.json:
{
...
"_moduleAliases": {
"#helpers": "dist/helpers",
"#root": "dist"
}
}
In tsconfig.json:
{
"compilerOptions": {
"baseUrl": "./src",
...
"paths": {
"#helpers/*": ["helpers/*"],
"#root/*": ["*"]
...
}
}
In jest.config.ts:
added roots, preset, the (.*) and $1 signs in moduleNameMapper*
{
roots: [
"<rootDir>/src/"
],
preset: "ts-jest",
transform: {
"^.+\\.(ts|tsx)$": "ts-jest",
},
moduleNameMapper: {
"#helpers/(.*)": "<rootDir>/src/helpers/$1",
"#root/(.*)": "<rootDir>/src/$1"
},
...
}
I think you don't have the routes properly configured in your tsconfig, since the paths lack the src folder (while they appear in your package.json module alias config):
Your tsconfig code:
"#config/*": ["config/*"],
"#interfaces/*": ["interfaces/*"],
"#services/*": ["services/*"]
How I think it should be:
"#config/*": ["src/config/*"],
"#interfaces/*": ["src/interfaces/*"],
"#services/*": ["src/services/*"]

compile typescript to js with webpack and keep directory structure

I want webpack to compile my typescript node project into js but I want it to maintain the directory structure and not bundle into 1 file.
Is this possible?
My structure is:
src
|_controllers
|_home
|_index.ts
|_ services
// etc.
And I want it to compile to:
dist
|_controllers
|_home
|_index.ts
|_ services
// etc.
currently my config is like this:
{
name: 'api',
target: 'node',
externals: getExternals(),
entry: isDevelopment ? [...entries] : entries,
devtool: !isDevelopment && 'cheap-module-source-map',
output: {
path: paths.appBuild,
filename: '[name].js',
libraryTarget: 'commonjs2'
},
plugins: [
new WriteFilePlugin(),
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1
}),
isProduction && new webpack.optimize.ModuleConcatenationPlugin()
]
}
Is it possible with webpack?
I can't use just tsc because I have a yarn workspaces monorepo and I might have a link reference like this:
import {something} from '#my/package';
#my/package does not exist in npm and only exists in the context of the monorepo, I can use node externals with webpack to include it in the bundle I don't think I can keep the folder structure this way.
Would the new typescript 3.0 project references solve this problem?
Understood your concerns. Looks it's completely doable by transpile-webpack-plugin. You may setup it as below:
const TranspilePlugin = require('transpile-webpack-plugin');
module.exports = {
// ...
entry: './src/controllers/home/index.ts',
output: {
path: __dirname + '/dist',
},
plugins: [new TranspilePlugin({ longestCommonDir: './src' })],
};
The plugin works well with webpack resolve.alias and externals. All the files directly or indirectly imported by the entry will be collected and compiled into the output dir seperately without bundling but with keeping the directory structure and file names. Just have a try. 🙂

"You may need an appropriate loader for this file type", webpack can't parse angular2 file

I'm trying to get a very simple Angular2 app working, with Webpack as a module bundler. I'm following this code, and I copied all the configuration files as they are, only changing file paths. However, when I run npm-start, I get the following error, which I think is a Webpack error:
ERROR in ./hello.js
Module parse failed: /home/marieficid/Documentos/cloud/cloud/hello.js Line 1: Unexpected token
You may need an appropriate loader to handle this file type.
| import {bootstrap} from "angular2/platform/browser";
| import {Component} from "angular2/core";
|
# ./app.ts 2:0-21
As a result, the Angular2 code in my app isn't loaded.
This is my app.ts:
import "./hello.js";
This is hello.js, where the error seems to be (which I take to mean that webpack parsed app.ts just fine):
import {bootstrap} from "angular2/platform/browser";
import {Component} from "angular2/core";
#Component({
selector: 'app',
template: '<div>Hello world</div>'
})
class App{}
bootstrap(App);
And this iswebpack.config.js:
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');
module.exports = {
entry: {
'app': './app.ts',
'vendor': './vendor.ts'
},
output: {
path: "./dist",
filename: "bundle.js"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.bundle.js'),
new HtmlWebpackPlugin({
inject: false,
template: './index.html'
})
],
resolve: {
extensions: ['', '.ts', '.js']
},
module: {
loaders: [
{ test: /\.ts$/, loader: 'ts-loader' },
],
noParse: [ path.join(__dirname, 'node_modules', 'angular2', 'bundles') ]
},
devServer: {
historyApiFallback: true
}
};
All these files and node_modules are in the same directory.
I have found similar questions online but nothing worked for me. I also didn't install babel because the sample code I'm using as base doesn't use it, but if it's necessary I'm will.
As suggested by #napstablook
Since in your webpack.config.js file you have
resolve: {
extensions: ['', '.ts', '.js']
},
Webpack will try to handle those .js files but it needs a specific loader to do so which is, if I'm not wrong, script-loader.
In your case the solution is as simple as deleting the .js files, or changing their extension to be .ts.
For me this issue occurred when I ran ng test,
please check below points,
Console will list out the files that is causing the error.
Check the html file is correctly mapped from the typescript.
styleUrls file should point to the CSS file not html, this is the mistake I
did.
this error also comes up for me in angular forms when i had patch value set then an extra = sign
ncont.controls[position].patchValue({[cardname]:file}) = file
which is a dumb part on me and angular for not telling me

Webpack with a client/server node setup?

I'm trying to set up a webpack-based flow for an Angular2 app with a node backend server. After many hours banging my head against it, I've managed to get the client to build happily, but I can not figure out how to now integrate my server build. My server uses generators, so must target ES6, and it needs to point to a different typings file (main.d.ts instead of browser.d.ts)..
My source tree looks like;
/
-- client/
-- -- <all my angular2 bits> (*.ts)
-- server/
-- -- <all my node/express bits> (*.ts)
-- webpack.config.js
-- typings/
-- -- browser.d.ts
-- -- main.d.ts
I thought perhaps just a tsconfig.json in the client and server folders would work, but no luck there. I also can't find a way to get html-webpack-plugin to ignore my server bundle and not inject it into index.html. My current tsconfig and webpack are below, but has anyone succeeded in getting webpack to bundle a setup like this? Any pointers would be much appreciated.
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"declaration": false,
"removeComments": true,
"noEmitHelpers": false,
"emitDecoratorMetadata": true,
"experimentalDecorators": true
},
"files": [
"typings/browser.d.ts",
"client/app.ts",
"client/bootstrap.ts",
"client/polyfills.ts"
]
}
and my webpack.config.js;
var Webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var Path = require('path');
module.exports = {
entry: {
'polyfills': Path.join(__dirname, 'client', 'polyfills.ts'),
'client': Path.join(__dirname, 'client', 'bootstrap.ts')
},
output: {
path: Path.join(__dirname, 'dist'),
filename: '[name].bundle.js'
},
resolve: {
extensions: ['', '.js', '.json', '.ts']
},
module: {
loaders: [
{
test: /\.ts$/,
loader: 'ts-loader',
query: {
ignoreDiagnostics: [
2403, // 2403 -> Subsequent variable declarations
2300, // 2300 -> Duplicate identifier
2374, // 2374 -> Duplicate number index signature
2375, // 2375 -> Duplicate string index signature
]
}
},
{ test: /\.json$/, loader: 'raw' },
{ test: /\.html$/, loader: 'raw' },
{ test: /\.css$/, loader: 'raw!postcss' },
{ test: /\.less$/, loSWE: 'raw!postcss!less' }
]
},
plugins: [
new HtmlWebpackPlugin({ template: 'client/index.html', filename: 'index.html' }),
new Webpack.optimize.CommonsChunkPlugin('common', 'common.bundle.js')
]
};
Personally, I tend to write my server side code in plain JS (with most of ES2015 available now in Node) and my Angular 2 app in Typescript, so this issue doesn't come up. However, you can get this to work with Webpack.
First, you should have two separate Webpack configs: one for your client-side code and one for the server side. It might be possible to do it with one config, but even if it were, it would likely be more trouble than it's worth. Make sure to set target: 'node' in your server-side config (target: 'web' is set automatically for the client side). And make sure you set an entry point for your server-side files (I don't see one above, but you will ultimately have this in a separate config anyway).
Second, you need to have multiple tsconfig files. By default, ts-loader will look for tsconfig.json in your root directory. However, you can tell specify another file by setting configFileName: 'path/to/tsconfig' in the options object or query string/object.
This may lead to another problem however. Your IDE will also look for your tsconfig.json file in your root directory. If you have two separate files, you will need some way to tell your IDE which one to use for any given file. The solution to this will depend on your IDE. Personally, I use Atom with atom-typescript, which is fantastic, but it looks like the multiple tsconfig files thing is still being worked on. Thankfully I have never had to worry about this problem.
As for the html-webpack-plugin issue, you won't have to worry about it since you won't include the plugin in your server-side config. However, just for reference, you can pass excludeChunks: ['someChunkName'] to omit certain chunks from being included in the script tags.

Resources