eslint should be able to resolve k6 modules via webpack.
eslint complains Unable to resolve path to module 'k6'.eslint(import/no-unresolved) for k6 imports via webpack. NPM and VSCode are able to resolve these paths
package.json:
{
"devDependencies": {
"@types/k6": "^0.26.0",
"@types/webpack": "^4.41.7",
"@typescript-eslint/eslint-plugin": "^2.23.0",
"@typescript-eslint/parser": "^2.23.0",
"k6": "0.0.0",
"webpack": "^4.42.0",
"webpack-cli": "^3.3.11",
},
"scripts": {
"k6:build": "webpack --config ./webpack.config.k6.ts",
"k6": "npm run k6:build && k6 run dist/load-test.js",
"k6:datadog": "docker-compose up -d && npm run k6:build && k6 run --out datadog dist/load-test.js",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx",
}
}
webpack.config.k6.ts
import { resolve } from 'path';
import { Configuration } from 'webpack';
import TerserPlugin from 'terser-webpack-plugin';
import Dotenv from 'dotenv-webpack';
const config: Configuration = {
entry: {
'load-test': './src/load-test.ts'
},
externals: [
{'k6': 'k6'},
{'k6/http': 'k6/http'},
{'k6/metrics': 'k6/metrics'},
{'k6/options': 'k6/options'}
],
output: {
filename: '[name].js',
libraryTarget: 'commonjs',
path: resolve(__dirname, 'dist'),
},
module: {
rules: [{test: /\.ts$/, loader: 'ts-loader'}],
},
resolve: {
extensions: ['.js', '.ts'],
},
target: 'node',
devtool: 'inline-source-map',
mode: process.env.NODE_ENV === 'dev' ? 'development' : 'production',
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
keep_fnames: true,
},
}),
],
},
plugins: [
new Dotenv({
path: './.env', // Path to .env file
safe: true, // load .env.example (defaults to "false" which does not use dotenv-safe)
systemvars: true
})
]
};
export default config;
.eslintrc.js
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: __dirname,
},
plugins: ['@typescript-eslint', 'jest', 'import'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:import/errors',
'plugin:import/typescript',
'prettier',
],
rules: {
'brace-style': 'error',
'no-console': 'off',
curly: ['error'],
'eol-last': ['error', 'always'],
'import/order': [
'error',
{
groups: [
'index',
'sibling',
'parent',
'internal',
'external',
'builtin',
],
alphabetize: {
order: 'asc',
},
},
],
'no-multiple-empty-lines': ['error', { max: 1 }],
quotes: ['error', 'single'],
semi: 'off',
'@typescript-eslint/semi': ['error', 'always'],
'@typescript-eslint/no-use-before-define': [
'error',
{
functions: false,
typedefs: false,
},
],
'@typescript-eslint/no-explicit-any': 'error',
"@typescript-eslint/camelcase": 'warn',
"@typescript-eslint/no-unused-vars": ['error', { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }],
},
};
tsconfig.json
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"target": "es2019",
"lib": ["es2019", "es2020.string"],
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"esModuleInterop": true,
"removeComments": true,
"noImplicitAny": true,
"strict": true,
"skipLibCheck": true,
"outDir": "dist",
"suppressImplicitAnyIndexErrors": true,
"types": ["jest", "node"]
},
"include": ["src/**/*.ts"]
}
load-test.ts
import {check, sleep} from 'k6'; // Unable to resolve path to module 'k6'. eslint(import/no-unresolved)
import http from 'k6/http'; // Unable to resolve path to module 'k6/http'. eslint(import/no-unresolved)
import {Counter} from 'k6/metrics'; // Unable to resolve path to module 'k6/metrics'. eslint(import/no-unresolved)
import {Options} from 'k6/options'; Unable to resolve path to module 'k6/options'. eslint(import/no-unresolved)
export const options: Options = {
/** Test stage specifications. Program of target VU stages. */
stages: [
{ duration: '30s', target: 50 }, // below normal load
{ duration: '30s', target: 50 },
{ duration: '30s', target: 150 }, // normal load
{ duration: '30s', target: 150 },
{ duration: '30s', target: 300 }, // around the breaking point
{ duration: '30s', target: 300 },
{ duration: '30s', target: 400 }, // beyond the breaking point
{ duration: '30s', target: 400 },
{ duration: '1m', target: 0 }, // scale down. Recovery stage.
],
/** Tags to set test wide across all metrics. */
tags: { 'randomId': `${process.env.RAND_ID}` }
};
const error500 = new Counter('500 error');
const authBearerToken = `Bearer ${process.env.API_AUTHORIZATION_SECRET}`;
const id = Math.floor(Math.random() * 101);
export function setup(): {} {
if(process.env.RAND_ID === 'true') {
return { id: id };
} else {
//The known success case is when Id = 2
return { id: 2 };
}
}
//Get details
export default (data: {id: number}): void => {
const url = 'https://api.test.app/get_details';
const headers = {'Content-Type': 'application/json', 'Authorization': authBearerToken};
const result = http.put(url, JSON.stringify(data), {headers: headers});
//Checks the result and creates a metric that is reported to stdout and datadog
check(result, {
'is status 200': (r) => r.status === 200,
});
if(result.status === 500) {
//Counts how many 500 error codes are returned
error500.add(1);
}
console.log(result.body);
sleep(1);
};
Hi @TaylorMichaelHall,
Thank you for your report! This should be resolvable by adding the following snippet to the top of your test file.
/*eslint import/no-unresolved: [2, { ignore: ['^k6.*'] }]*/
The reason as to why this is happening is because k6 modules are not really node modules, but rather built-in modules provided by the k6 runtime. You are able to resolve them in vscode because it automatically installs the package types from definitely-typed.
Thank you for the thorough answer @simskij!