@qavajs/playwright-runner-adapter 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,14 @@
1
+ # Change Log
2
+
3
+ All notable changes to the "@qavajs/steps-playwright" will be documented in this file.
4
+
5
+ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
6
+
7
+ :rocket: - new feature
8
+ :beetle: - bugfix
9
+ :x: - deprecation/removal
10
+ :pencil: - chore
11
+ :microscope: - experimental
12
+
13
+ ## [0.1.0]
14
+ - :rocket: initial implementation
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 @qavajs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,70 @@
1
+ [![npm version](https://badge.fury.io/js/@qavajs%2Fplaywright-runner-adapter.svg)](https://badge.fury.io/js/@qavajs%2Fplaywright-runner-adapter)
2
+
3
+ # @qavajs/playwright-runner-adapter
4
+ Adapter to run cucumberjs tests via playwright test runner
5
+
6
+ ## Installation
7
+
8
+ `npm install @qavajs/playwright-runner-adapter`
9
+
10
+ ## Basic Configuration
11
+
12
+ ### Create cucumber config file
13
+ Set _paths_ and _require_ properties
14
+ ```typescript
15
+ export default {
16
+ paths: ['test/features/*.feature'],
17
+ require: ['test/step_definitions/*.ts']
18
+ }
19
+ ```
20
+ ### Set testDir
21
+ Set testDir to adapter
22
+ ```typescript
23
+ export default defineConfig({
24
+ testDir: resolve('node_modules/@qavajs/playwright-runner-adapter/adapter')
25
+ });
26
+ ```
27
+ ### Add cucumber config
28
+ Set CONFIG and PROFILE environment variables e.g via dotenv library
29
+ ```dotenv
30
+ CONFIG=test/cucumber.ts
31
+ PROFILE=default
32
+ ```
33
+
34
+ ## Advanced Configuration
35
+ ### Customizing test instance
36
+ Custom test instance can be passed to world constructor as _test_ property.
37
+ And then fixtures can be connected with world instance via _init_ property.
38
+ ```typescript
39
+ import { test as base } from '@playwright/test';
40
+ import { SettingsPage } from './settings-page';
41
+ import { setWorldConstructor } from '@cucumber/cucumber';
42
+ import { PlaywrightWorld } from '@qavajs/playwright-runner-adapter/PlaywrightWorld';
43
+
44
+ type MyFixtures = {
45
+ settingsPage: SettingsPage;
46
+ };
47
+
48
+ const customTest = base.extend<MyFixtures>({
49
+ settingsPage: async ({ page }, use) => {
50
+ await use(new SettingsPage(page));
51
+ },
52
+ });
53
+
54
+ class ExtendedPlaywrightWorld extends PlaywrightWorld {
55
+ settingsPage: SettingsPage;
56
+ constructor(options: any) {
57
+ super(options);
58
+ }
59
+
60
+ test = customTest;
61
+
62
+ // init arrow function connects fixtures with Cucumber world instance
63
+ init = ({ settingsPage }) => {
64
+ this.settingsPage = settingsPage;
65
+ }
66
+
67
+ }
68
+ ```
69
+
70
+
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PlaywrightWorld = void 0;
4
+ const test_1 = require("@playwright/test");
5
+ class PlaywrightWorld {
6
+ constructor(options) {
7
+ this.test = test_1.test;
8
+ this.init = ({ browser, context, page }) => {
9
+ this.browser = browser;
10
+ this.context = context;
11
+ this.page = page;
12
+ };
13
+ this.log = options.log;
14
+ this.attach = options.attach;
15
+ this.parameters = options.parameters;
16
+ }
17
+ }
18
+ exports.PlaywrightWorld = PlaywrightWorld;
19
+ //# sourceMappingURL=PlaywrightWorld.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PlaywrightWorld.js","sourceRoot":"","sources":["../src/PlaywrightWorld.ts"],"names":[],"mappings":";;;AAAA,2CAA0F;AAE1F,MAAa,eAAe;IAWxB,YAAY,OAAY;QAFxB,SAAI,GAAG,WAAI,CAAC;QAQZ,SAAI,GAAG,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAA6D,EAAE,EAAE;YAC7F,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;YACvB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;YACvB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACrB,CAAC,CAAA;QATG,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IACzC,CAAC;CAOJ;AAtBD,0CAsBC"}
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ var _a, _b;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const loader_1 = require("./loader");
5
+ const config = require((_a = process.env.CONFIG) !== null && _a !== void 0 ? _a : 'config.js');
6
+ const profile = (_b = process.env.PROFILE) !== null && _b !== void 0 ? _b : 'default';
7
+ const resolvedConfig = config[profile];
8
+ const features = (0, loader_1.loadFeatures)(resolvedConfig.paths);
9
+ const supportCodeLibrary = (0, loader_1.loadStepDefinitions)(resolvedConfig.require);
10
+ function log(data) {
11
+ console.log(data);
12
+ }
13
+ function attach(body, details) {
14
+ var _a, _b;
15
+ const fileName = (_a = details.fileName) !== null && _a !== void 0 ? _a : 'attachment';
16
+ const contentType = (_b = details.mediaType) !== null && _b !== void 0 ? _b : 'text/plain';
17
+ this.test.info().attach(fileName, { body, contentType });
18
+ }
19
+ const world = new supportCodeLibrary.World({
20
+ log,
21
+ attach
22
+ });
23
+ const test = world.test;
24
+ for (const feature of features) {
25
+ const tests = feature.tests;
26
+ test.describe(feature.feature, async () => {
27
+ for (const beforeAllHook of supportCodeLibrary.beforeTestRunHookDefinitions) {
28
+ test.beforeAll(async () => {
29
+ await beforeAllHook.code.apply({});
30
+ });
31
+ }
32
+ test.beforeEach(world.init);
33
+ for (const testCase of tests) {
34
+ const tag = testCase.tags.map(tag => tag.name);
35
+ test(testCase.name, { tag }, async () => {
36
+ for (const beforeHook of supportCodeLibrary.beforeTestCaseHookDefinitions) {
37
+ if (beforeHook.appliesToTestCase(testCase)) {
38
+ await test.step('Before', async () => {
39
+ await beforeHook.code.apply(world, [testCase]);
40
+ });
41
+ }
42
+ }
43
+ for (const pickleStep of testCase.steps) {
44
+ await test.step(pickleStep.text, async () => {
45
+ for (const beforeStep of supportCodeLibrary.beforeTestStepHookDefinitions) {
46
+ if (beforeStep.appliesToTestCase(testCase)) {
47
+ await beforeStep.code.apply(world);
48
+ }
49
+ }
50
+ const steps = supportCodeLibrary.stepDefinitions
51
+ .filter(stepDefinition => stepDefinition.matchesStepName(pickleStep.text));
52
+ if (steps.length === 0)
53
+ throw new Error(`Step '${pickleStep.text}' is not defined`);
54
+ if (steps.length > 1)
55
+ throw new Error(`'${pickleStep.text}' matches multiple step definitions`);
56
+ const [step] = steps;
57
+ const { parameters } = await step.getInvocationParameters({
58
+ // @ts-ignore
59
+ step: {
60
+ text: pickleStep.text,
61
+ argument: pickleStep.argument
62
+ },
63
+ world
64
+ });
65
+ const result = { status: 'passed' };
66
+ try {
67
+ await step.code.apply(world, parameters);
68
+ }
69
+ catch (err) {
70
+ result.status = 'failed';
71
+ result.error = err;
72
+ }
73
+ for (const afterStep of supportCodeLibrary.afterTestStepHookDefinitions) {
74
+ if (afterStep.appliesToTestCase(testCase)) {
75
+ await afterStep.code.apply(world, [{ result }]);
76
+ }
77
+ }
78
+ if (result.error)
79
+ throw result.error;
80
+ });
81
+ }
82
+ for (const afterHook of supportCodeLibrary.afterTestCaseHookDefinitions) {
83
+ if (afterHook.appliesToTestCase(testCase)) {
84
+ await test.step('After', async () => {
85
+ await afterHook.code.apply(world, [testCase]);
86
+ });
87
+ }
88
+ }
89
+ });
90
+ }
91
+ for (const afterAllHook of supportCodeLibrary.afterTestRunHookDefinitions) {
92
+ test.afterAll(async function () {
93
+ await afterAllHook.code.apply({});
94
+ });
95
+ }
96
+ });
97
+ }
98
+ //# sourceMappingURL=adapter.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.spec.js","sourceRoot":"","sources":["../src/adapter.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadStepDefinitions = exports.loadFeatures = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const gherkin_1 = require("@cucumber/gherkin");
7
+ const messages_1 = require("@cucumber/messages");
8
+ const cucumber_1 = require("@cucumber/cucumber");
9
+ const glob_1 = require("glob");
10
+ const PlaywrightWorld_1 = require("./PlaywrightWorld");
11
+ const uuidFn = messages_1.IdGenerator.uuid();
12
+ const builder = new gherkin_1.AstBuilder(uuidFn);
13
+ const matcher = new gherkin_1.GherkinClassicTokenMatcher();
14
+ const parser = new gherkin_1.Parser(builder, matcher);
15
+ function loadFeatures(globPattern) {
16
+ const files = (0, glob_1.globSync)(globPattern);
17
+ return files.map(file => {
18
+ var _a;
19
+ const filePath = (0, node_path_1.resolve)(file);
20
+ const gherkinDocument = parser.parse((0, node_fs_1.readFileSync)(filePath, 'utf-8'));
21
+ return {
22
+ feature: (_a = gherkinDocument.feature) === null || _a === void 0 ? void 0 : _a.name,
23
+ tests: (0, gherkin_1.compile)(gherkinDocument, file, uuidFn)
24
+ };
25
+ });
26
+ }
27
+ exports.loadFeatures = loadFeatures;
28
+ function loadStepDefinitions(globPattern) {
29
+ cucumber_1.supportCodeLibraryBuilder.reset(process.cwd(), uuidFn);
30
+ cucumber_1.supportCodeLibraryBuilder.methods.setWorldConstructor(PlaywrightWorld_1.PlaywrightWorld);
31
+ const files = (0, glob_1.globSync)(globPattern);
32
+ for (const file of files) {
33
+ const filePath = (0, node_path_1.resolve)(file);
34
+ require(filePath);
35
+ }
36
+ return cucumber_1.supportCodeLibraryBuilder.finalize();
37
+ }
38
+ exports.loadStepDefinitions = loadStepDefinitions;
39
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":";;;AAAA,qCAAuC;AACvC,yCAAoC;AACpC,+CAA4F;AAC5F,iDAAiD;AACjD,iDAA+D;AAC/D,+BAAgC;AAChC,uDAAkD;AAElD,MAAM,MAAM,GAAG,sBAAW,CAAC,IAAI,EAAE,CAAA;AACjC,MAAM,OAAO,GAAG,IAAI,oBAAU,CAAC,MAAM,CAAC,CAAA;AACtC,MAAM,OAAO,GAAG,IAAI,oCAA0B,EAAE,CAAC;AACjD,MAAM,MAAM,GAAG,IAAI,gBAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AAE3C,SAAgB,YAAY,CAAC,WAAqB;IAC9C,MAAM,KAAK,GAAG,IAAA,eAAQ,EAAC,WAAW,CAAC,CAAC;IACpC,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;;QACpB,MAAM,QAAQ,GAAG,IAAA,mBAAO,EAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,eAAe,GAAG,MAAM,CAAC,KAAK,CAAC,IAAA,sBAAY,EAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;QACtE,OAAO;YACH,OAAO,EAAE,MAAA,eAAe,CAAC,OAAO,0CAAE,IAAI;YACtC,KAAK,EAAE,IAAA,iBAAO,EAAC,eAAe,EAAE,IAAI,EAAE,MAAM,CAAC;SAChD,CAAA;IACL,CAAC,CAAC,CAAC;AACP,CAAC;AAVD,oCAUC;AAED,SAAgB,mBAAmB,CAAC,WAAqB;IACrD,oCAAyB,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;IACvD,oCAAyB,CAAC,OAAO,CAAC,mBAAmB,CAAC,iCAAe,CAAC,CAAC;IACvE,MAAM,KAAK,GAAG,IAAA,eAAQ,EAAC,WAAW,CAAC,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,IAAA,mBAAO,EAAC,IAAI,CAAC,CAAC;QAC/B,OAAO,CAAC,QAAQ,CAAC,CAAC;IACtB,CAAC;IACD,OAAO,oCAAyB,CAAC,QAAQ,EAAE,CAAC;AAChD,CAAC;AATD,kDASC"}
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { PlaywrightWorld } from './src/PlaywrightWorld';
package/index.js ADDED
@@ -0,0 +1,4 @@
1
+ const { PlaywrightWorld } = require('./adapter/PlaywrightWorld');
2
+ module.exports = {
3
+ PlaywrightWorld
4
+ }
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@qavajs/playwright-runner-adapter",
3
+ "version": "0.1.0",
4
+ "description": "adapter for playwright test runner",
5
+ "scripts": {
6
+ "build": "tsc",
7
+ "test:ui": "playwright test --config test/playwright.config.ts --ui",
8
+ "test:e2e": "playwright test --config test/playwright-e2e.config.ts"
9
+ },
10
+ "author": "Alexandr Galichenko",
11
+ "license": "MIT",
12
+ "dependencies": {
13
+ "@cucumber/gherkin": "^28.0.0",
14
+ "@cucumber/messages": "^24.1.0",
15
+ "glob": "^10.3.12"
16
+ },
17
+ "devDependencies": {
18
+ "@cucumber/cucumber": "^10.3.2",
19
+ "@playwright/test": "^1.42.1",
20
+ "@types/node": "^20.12.2",
21
+ "typescript": "^5.4.3"
22
+ }
23
+ }
@@ -0,0 +1,25 @@
1
+ import { APIRequestContext, Browser, BrowserContext, Page, test } from '@playwright/test';
2
+
3
+ export class PlaywrightWorld {
4
+ page!: Page;
5
+ context!: BrowserContext;
6
+ browser!: Browser;
7
+ browserName!: string;
8
+ request!: APIRequestContext;
9
+ log!: (data: any) => void;
10
+ attach!: (data: any, details?: { filename?: string, mediaType: string }) => void;
11
+ parameters!: string;
12
+ test = test;
13
+
14
+ constructor(options: any) {
15
+ this.log = options.log;
16
+ this.attach = options.attach;
17
+ this.parameters = options.parameters;
18
+ }
19
+
20
+ init = ({ browser, context, page }: { browser: Browser, context: BrowserContext, page: Page }) => {
21
+ this.browser = browser;
22
+ this.context = context;
23
+ this.page = page;
24
+ }
25
+ }
@@ -0,0 +1,97 @@
1
+ import {loadStepDefinitions, loadFeatures} from './loader';
2
+
3
+ const config = require(process.env.CONFIG ?? 'config.js');
4
+ const profile = process.env.PROFILE ?? 'default';
5
+
6
+ const resolvedConfig = config[profile];
7
+ const features = loadFeatures(resolvedConfig.paths);
8
+ const supportCodeLibrary = loadStepDefinitions(resolvedConfig.require);
9
+
10
+ function log(data: any) {
11
+ console.log(data);
12
+ }
13
+ function attach(this: { test: any }, body: any, details: any) {
14
+ const fileName = details.fileName ?? 'attachment';
15
+ const contentType = details.mediaType ?? 'text/plain';
16
+ this.test.info().attach(fileName, { body, contentType })
17
+ }
18
+ const world = new supportCodeLibrary.World({
19
+ log,
20
+ attach
21
+ });
22
+
23
+ const test = world.test;
24
+
25
+ for (const feature of features) {
26
+ const tests = feature.tests;
27
+ test.describe(feature.feature as string, async () => {
28
+ for (const beforeAllHook of supportCodeLibrary.beforeTestRunHookDefinitions) {
29
+ test.beforeAll(async () => {
30
+ await beforeAllHook.code.apply({});
31
+ });
32
+ }
33
+
34
+ test.beforeEach(world.init);
35
+
36
+ for (const testCase of tests) {
37
+ const tag = testCase.tags.map(tag => tag.name);
38
+
39
+ test(testCase.name, {tag}, async () => {
40
+ for (const beforeHook of supportCodeLibrary.beforeTestCaseHookDefinitions) {
41
+ if (beforeHook.appliesToTestCase(testCase)) {
42
+ await test.step('Before', async () => {
43
+ await beforeHook.code.apply(world, [testCase]);
44
+ });
45
+ }
46
+ }
47
+ for (const pickleStep of testCase.steps) {
48
+ await test.step(pickleStep.text, async () => {
49
+ for (const beforeStep of supportCodeLibrary.beforeTestStepHookDefinitions) {
50
+ if (beforeStep.appliesToTestCase(testCase)) {
51
+ await beforeStep.code.apply(world);
52
+ }
53
+ }
54
+ const steps = supportCodeLibrary.stepDefinitions
55
+ .filter(stepDefinition => stepDefinition.matchesStepName(pickleStep.text));
56
+ if (steps.length === 0) throw new Error(`Step '${pickleStep.text}' is not defined`);
57
+ if (steps.length > 1) throw new Error(`'${pickleStep.text}' matches multiple step definitions`);
58
+ const [step] = steps;
59
+ const { parameters} = await step.getInvocationParameters({
60
+ // @ts-ignore
61
+ step: {
62
+ text: pickleStep.text,
63
+ argument: pickleStep.argument
64
+ },
65
+ world
66
+ });
67
+ const result: { status: string, error?: any } = { status: 'passed' };
68
+ try {
69
+ await step.code.apply(world, parameters);
70
+ } catch (err) {
71
+ result.status = 'failed';
72
+ result.error = err;
73
+ }
74
+ for (const afterStep of supportCodeLibrary.afterTestStepHookDefinitions) {
75
+ if (afterStep.appliesToTestCase(testCase)) {
76
+ await afterStep.code.apply(world, [{result}]);
77
+ }
78
+ }
79
+ if (result.error) throw result.error;
80
+ });
81
+ }
82
+ for (const afterHook of supportCodeLibrary.afterTestCaseHookDefinitions) {
83
+ if (afterHook.appliesToTestCase(testCase)) {
84
+ await test.step('After', async () => {
85
+ await afterHook.code.apply(world, [testCase]);
86
+ });
87
+ }
88
+ }
89
+ })
90
+ }
91
+ for (const afterAllHook of supportCodeLibrary.afterTestRunHookDefinitions) {
92
+ test.afterAll(async function () {
93
+ await afterAllHook.code.apply({});
94
+ });
95
+ }
96
+ });
97
+ }
package/src/loader.ts ADDED
@@ -0,0 +1,35 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { AstBuilder, compile, GherkinClassicTokenMatcher, Parser } from '@cucumber/gherkin';
4
+ import { IdGenerator } from '@cucumber/messages';
5
+ import { supportCodeLibraryBuilder } from '@cucumber/cucumber';
6
+ import { globSync } from 'glob';
7
+ import {PlaywrightWorld} from './PlaywrightWorld';
8
+
9
+ const uuidFn = IdGenerator.uuid()
10
+ const builder = new AstBuilder(uuidFn)
11
+ const matcher = new GherkinClassicTokenMatcher();
12
+ const parser = new Parser(builder, matcher)
13
+
14
+ export function loadFeatures(globPattern: string[]) {
15
+ const files = globSync(globPattern);
16
+ return files.map(file => {
17
+ const filePath = resolve(file);
18
+ const gherkinDocument = parser.parse(readFileSync(filePath, 'utf-8'));
19
+ return {
20
+ feature: gherkinDocument.feature?.name,
21
+ tests: compile(gherkinDocument, file, uuidFn)
22
+ }
23
+ });
24
+ }
25
+
26
+ export function loadStepDefinitions(globPattern: string[]) {
27
+ supportCodeLibraryBuilder.reset(process.cwd(), uuidFn);
28
+ supportCodeLibraryBuilder.methods.setWorldConstructor(PlaywrightWorld);
29
+ const files = globSync(globPattern);
30
+ for (const file of files) {
31
+ const filePath = resolve(file);
32
+ require(filePath);
33
+ }
34
+ return supportCodeLibraryBuilder.finalize();
35
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "include": [
3
+ "src/**/*.ts"
4
+ ],
5
+ "exclude": [
6
+ "test/**/*.spec.ts"
7
+ ],
8
+ "compilerOptions": {
9
+ "target": "es2017",
10
+ "module": "node16",
11
+ "moduleResolution": "node16",
12
+ "outDir": "./adapter",
13
+ "esModuleInterop": true,
14
+ "sourceMap": true,
15
+ "forceConsistentCasingInFileNames": true,
16
+ "strict": true,
17
+ "skipLibCheck": true,
18
+ "types": [
19
+ "node"
20
+ ]
21
+ }
22
+ }