With Webpack, I can do
resolve: {
modules: ['foo', 'bar', 'node_modules']
}
to make it possible to import foo from 'foo' without having to know the relative path to the foo folder.
How do I tell VS Code the same thing, so that it can recognize, navigate to, and give me intellisense for that import statement?
To do that you need to create a jsconfig.json at your projects root direcotry.
Now you can set the compilerOptions.paths attribute to enable using webpack aliases.
From the docs:
For IntelliSense to work with webpack aliases, you need to specify the paths keys with a glob pattern.
For example, for alias 'ClientApp':
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"ClientApp/*": ["./ClientApp/*"]
}
}
}
and then to use the alias
import Something from 'ClientApp/foo'
Related
I have an import import icon from 'src/assets/icon.png', which I can't resolve.
I have "baseUrl": "." in jsconfig.json and
"settings": {
"import/resolver": {
"node": {
"paths": ["."]
}
}
}
in .eslintrc, but the thing is that if I use absolute import this way I get an error from Vite - [plugin:vite:import-analysis] Failed to resolve import "src/assets/icon.png" from "src\lib\Logo.svelte". Does the file exist?
At the same time if I add a forward slash before src in import like so import icon from '/src/assets/icon.png', it will work fine with NO error from vite, but eslint/no-unresolved-imports rule will give me a linting error.
I checked vite docs, but the only thing they offer is to use an alias for the path, which I'm not willing to do. Another workaround could be disabling the eslint rule, which is not an option for me either.
Question: Is there a way to resolve this path 'src/assets/icon.png' using "import/resolver" or vite's settings?
I found this page helpful in getting my setup working for absolute/aliased imports with Vite + React, but maybe it will be helpful to you too.
https://dev.to/abdeldjalilhachimi/how-to-avoid-long-path-import-using-react-with-ts-and-vite-4e2h
You don't have to define an alias for every folder - instead you use a dynamics alias that reads the directory name.
Add this to your vite.config.ts to set up the dynamic alias:
import * as path from 'path';
//...
export default defineConfig({
// ...config
resolve: {
alias: {
'~': path.resolve(__dirname, 'src'),
},
},
});
Then in your tsconfig.json you can define the alias like so:
"baseUrl": "./src",
"paths": {
"~/*": ["./*"]
},
The baseUrl by itself almost worked well enough, but this solution seems to be more robust and lets me do exactly the kind of asset and module imports that you're talking about.
import logoPNG from '~/assets/logo.png';
I am developing the Argdown VSCode extension. The Argdown parser can be configured using either argdown.config.json files or argdown.config.js files exporting a config object. Using Javascript files is the easiest way to allow users to add custom plugins to the Argdown parser.
If the user tells the parser to use a Javascript file, the file is loaded using import-fresh, (which uses node's require, but deletes the cached version.
Using the Argdown commandline tool (#argdown/cli) this works fine, but in the VSCode extension the module of the config file can not be found. The extension is using absolute file paths to require the config module (e.g. "C:\Users\my-username\projects\my-argdown-project\argdown.config.js"). These paths work with import-fresh outside of the VScode extension.
Is there a security restriction for VSCode extensions that does not allow to require modules with absolute file paths? Or is there some other reason why this does not work?
This was not related to VSCode. The problem was caused by bundling up import-fresh with webpack. I thought that webpack would ignore dynamic imports, but it did not.
I was lucky: Since last month, webpack supports "magic comments" for require (not only for import). So I can use:
require(/* webpackIgnore: true */ file);
You have to activate magic comments support in your webpack config:
module.exports = {
parser: {
javascript: {
commonjsMagicComments: true,
},
},
}
Now the next question is how to add the magic comments to the import-fresh package. For that I used the string-replace-loader:
module.exports = {
module: {
rules: {
{
enforce: "pre",
test: /import-fresh[\/\\]index\.js/,
loader: "string-replace-loader",
options: {
search:
"return parent === undefined ? require(filePath) : parent.require(filePath);",
replace:
"return parent === undefined ? require(/* webpackIgnore: true */ filePath) : parent.require(/* webpackIgnore: true */ filePath);",
},
},
}
}
}
After that, I could load the argdown.config.js files again, even after bundling everything with webpack.
I am just getting started with the Jest test framework and while straight up unit tests work fine, I am having massive issues testing any component that in its module (ES module via babel+webpack) requires a HTML file.
Here is an example:
import './errorHandler.scss';
import template from './errorHandler.tmpl';
class ErrorHandler {
...
I am loading the component specific SCSS file which I have set in Jest's package.json config to return an empty object but when Jest tries to run the import template from './errorHandler.tmpl'; line it breaks saying:
/Users/jannis/Sites/my-app/src/scripts/errorHandler/errorHandler.tmpl.html:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){<div class="overlay--top">
^
SyntaxError: Unexpected token <
at transformAndBuildScript (node_modules/jest-runtime/build/transform.js:284:10)
My Jest config from package.json is as follows:
"jest": {
"setupTestFrameworkScriptFile": "<rootDir>/test/setupFile.js",
"moduleDirectories": ["node_modules"],
"moduleFileExtensions": ["js", "json", "html", "scss"],
"moduleNameMapper": {
"^.+\\.scss$": "<rootDir>/test/styleMock.js"
}
}
It seems that the webpack html-loader is not working correctly with Jest but I can't find any solution on how to fix this.
Does anyone know how I can make these html-loader imports work in my tests? They load my lodash template markup and i'd rather not have these at times massive HTML chunks in my .js file so i can omit the import template from x part.
PS: This is not a react project, just plain webpack, babel, es6.
I encountered this specific problem recently and creating your own transform preprocesser will solve it. This was my set up:
package.json
"jest": {
"moduleFileExtensions": [
"js",
"html"
],
"transform": {
"^.+\\.js$": "babel-jest",
"^.+\\.html$": "<rootDir>/test/utils/htmlLoader.js"
}
}
NOTE: babel-jest is normally included by default, but if you specify a custom transform preprocessor, you seem to have to include it manually.
test/utils/htmlLoader.js:
const htmlLoader = require('html-loader');
module.exports = {
process(src, filename, config, options) {
return htmlLoader(src);
}
}
A bit late to the party, but wanted to add that there is also this html-loader-jest npm package out there to do this if you wanted to go that route.
Once you npm install it you will add it to your jest configuration with
"transform": {
"^.+\\.js$": "babel-jest",
"^.+\\.html?$": "html-loader-jest"
}
For Jest > 28.x.x with html-loader:
Create a custom transformer as documented here.
jest/html-loader.js
const htmlLoader = require("html-loader");
module.exports = {
process(sourceText) {
return {
code: `module.exports = ${htmlLoader(sourceText)};`,
};
},
};
Add it to your jest config.
jest.config.js
...
// A map from regular expressions to paths to transformers
transform: {
"^.+\\.html$": "<rootDir>/jest/html-loader.js",
},
...
It will fix the error : Invalid return value: process() or/and processAsync() method of code transformer found at "<PATH>" should return an object or a Promise resolving to an object.
Maybe your own preprocessor file will be the solution:
ScriptPreprocessor
Custom-preprocessors
scriptpreprocessor: The path to a module that provides a synchronous function from pre-processing source files. For example, if you wanted to be able to use a new language feature in your modules or tests that isn't yet supported by node (like, for example, ES6 classes), you might plug in one of many transpilers that compile ES6 to ES5 here.
I created my own preprocessor when I had a problems with my tests after added transform-decorators-legacy to my webpack module loaders.
html-loader-jest doesn't work for me. My workaround for this:
"transform": {
'\\.(html)$': '<rootDir>/htmlTemplateMock.html'
}
htmlTemplateMock.html is empty file
For Jest 28+ you can use jest-html-loader to make Jest work with code that requires HTML files.
npm install --save-dev jest-html-loader
In your jest config, add it as a transformer for .HTML files:
"transform": {
"^.+\\.html?$": "jest-html-loader"
},
Can the Typescript compiler ignore the cannot find module 'x' error on import expressions such as:
//How to tell the compiler that this module does exists
import sql = require('sql');
There are multiple npm libraries such as node sql that doesn't have existing typings
Is there a way to tell the compiler to ignore this error other than creating a new definition file with the declare module x ... ?
As of TypeScript 2.6 (released on Oct 31, 2017), now there is a way to ignore all errors from a specific line using // #ts-ignore comments before the target line.
The mendtioned documentation is succinct enough, but to recap:
// #ts-ignore
const s : string = false
disables error reporting for this line.
However, this should only be used as a last resort when fixing the error or using hacks like (x as any) is much more trouble than losing all type checking for a line.
As for specifying certain errors, the current (mid-2018) state is discussed here, in Design Meeting Notes (2/16/2018) and further comments, which is basically
"no conclusion yet"
and strong opposition to introducing this fine tuning.
If you just want to bypass the compiler, you can create a .d.ts file for that module, for instance, you could create a sql.d.ts file and inside have this:
declare module "sql" {
let _sql: any;
export = _sql;
}
Solved This
By default #ts-check looks for definitions of a module, either its your own code or external libraries.
Since we are not using ES6 style modules, then we must we are using
commonjs, check my jsconfig.json file for help.
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"lib": ["es5", "es6", "es7"]
},
"include": ["src/**/*"],
"exclude": ["node_modules"],
"typeAcquisition": { "enable": true }
}
So I have a project that looks something like this:
app/
bin/
lib/
src/
main/
submodule.ts
utilities/
common.ts
main.ts
tsconfig.json
gulpfile.js
and app/src/main/submodule.ts needs to import app/src/utilities/common.ts. I am trying to use the ES6 syntax for this. Thus I expect something like this in submodule.ts:
import {common} from '/utilities/common';
Where the root / is app/src/ since that is where tsconfig is found. Yes, app/src/utilities/common.ts does export a module named common.
The problem is that I get "cannot find module '/utilities/common'" errors. I have tried a variety of things:
utilities/common
/src/utilities/common
/app/src/utilities/common
None of these work. A relative path of ../utilities/common does work, but relative paths for common modules is a maintenance nightmare.
It may be worth noting that I just updated from TS 1.5 to 1.6: using utilities/common had worked in 1.5. I cannot find any mention of a breaking change along these lines in the 1.6 notes, though.
I mention the gulpfile.ts and other folders because ultimately I want Gulp to get the TS files from src and put the compiled JS files in bin. I am reasonably confident that I have correctly configured Gulp for this, using gulp-typescript, but for completion's sake, here are my tsconfig.json and gulpfile.js.
tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"noEmitOnError": true
},
"filesGlob": [
"./**/*.ts",
"!./typings/**/*.ts"
]
}
gulpfile.js
var gulp = require('gulp');
var ts = require('gulp-typescript');
var less = require('gulp-less');
var sourcemaps = require('gulp-sourcemaps');
var merge = require('merge2');
var path = require('path');
var tsProject = ts.createProject('src/tsconfig.json', { noExternalResolve: true });
gulp.task('html', function () {
gulp.src([
'src/**/*.html',
])
.pipe(gulp.dest('bin'));
});
gulp.task('typescript', function () {
tsProject.src()
.pipe(sourcemaps.init())
.pipe(ts(tsProject))
.js
.pipe(sourcemaps.write())
.pipe(gulp.dest('bin'));
});
gulp.task('less', function () {
gulp.src([
'src/**/*.less',
])
.pipe(sourcemaps.init())
.pipe(less())
.pipe(sourcemaps.write())
.pipe(gulp.dest('bin'))
});
gulp.task('default', ['html', 'typescript', 'less']);
Finally solved this. Per What's New, 1.6 changed module resolution to behave like Node's. I have not yet investigated Node's module resolution to determine if a fix is possible using that behavior, but I have found a workaround:
The old behavior can be triggered by specifying "moduleResolution": "classic" in tsconfig.json.
Module resolution is performed relative to the current file if the path starts with ./ or ../.
Here is a quick example using:
/
/src/
/src/thing.ts
/main/
/main/submodule.ts
/utilities/
/utilities/common.ts
So the correct statement to import common.ts into submodule.ts would be:
import {common} from '../utilities/common';
You can also use the following root-path (note that there is no leading / or any .s here):
import {common} from 'src/utilities/common';
This works for me in Visual Studio code, with the parent folder of src opened as the working folder. In my case I am targeting ES5 with UMD modules.
You can also resolve a module if it can be found by traversing up from the current location (this is a feature of NodeJS). So you can import thing.ts into submodule.ts using:
import {something} from 'thing';
The resolver will check the current folder, then the parent, then the parent's parent... and so on.
Absolute vs Relative Paths
When it comes to links on web pages, I'm in agreement with you that absolute paths are easier to maintain, especially where resources are shared at multiple levels.
When it comes to modules, I'm not sure I see the same maintenance problem as the paths here are "relative to the file that the import statement appears in" not relative to the web page. I wonder if this may be the application of a very sensible rule in the wrong place.