From 468a617e3b233cf6956b386ea009833caac060a5 Mon Sep 17 00:00:00 2001 From: Jeffrey Lau Date: Mon, 15 May 2023 17:39:43 +0800 Subject: [PATCH] refactor(test): Make test work better --- .github/workflows/build.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/test-e2e.yml | 9 +-- .github/workflows/test.yml | 4 +- Makefile | 8 ++- packages/legacy/codecept.conf.ts | 24 +++----- packages/legacy/jest.config.ts | 55 +++++++++++++++---- packages/legacy/package.json | 15 ++--- .../src/smart/model/editor/commands/data.ts | 4 +- .../smart/model/editor/commands/elements.ts | 10 ++-- .../src/smart/model/editor/commands/import.ts | 2 +- .../src/smart/model/editor/commands/page.ts | 6 +- packages/legacy/steps_file.ts | 10 ++++ packages/legacy/tsconfig.json | 22 +++++++- tsconfig.base.json | 27 +++++++++ tsconfig.json | 23 ++++++++ 17 files changed, 169 insertions(+), 56 deletions(-) create mode 100644 packages/legacy/steps_file.ts create mode 100644 tsconfig.base.json create mode 100644 tsconfig.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b85d6f9e..8c912c9e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ jobs: run: | if command -v shasum; then sum() { shasum -a 256 "$@"; } ; else sum() { sha256sum "$@"; }; fi fragment=$(< package.json command grep 'version"\|"name"\|"files"\|"main"\|"build' | sum | command head -c8) - echo "key=build-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('packages/**/src/**', '**/.swcrc', '**/.npmrc', '**/tsconfig*.json', '**/pnpm-lock.yaml', '.github/workflows/build.yml') }}-${fragment}" | tee -a $GITHUB_OUTPUT + echo "key=build-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/src/**', '**/.swcrc', '**/.npmrc', '**/tsconfig*.json', '**/pnpm-lock.yaml', '.github/workflows/build.yml') }}-${fragment}" | tee -a $GITHUB_OUTPUT shell: bash - name: Restore build cache diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a367a212..3bf3a2c8 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -33,7 +33,7 @@ jobs: uses: actions/cache@v3 with: path: ${{ env.CACHE_TEST_DIR }} - key: lint-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/*eslintrc*', '**/.*eslintrc*', 'packages/**', '**/pnpm-lock.yaml') }}-${{ steps.cache-key.outputs.fragment }} + key: lint-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/*eslintrc*', '**/.*eslintrc*', '**/src/**', '**/pnpm-lock.yaml') }}-${{ steps.cache-key.outputs.fragment }} - uses: pnpm/action-setup@v2 if: steps.test_cache.outputs.cache-hit != 'true' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7a356cdf..60b3b237 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -49,7 +49,7 @@ jobs: run: | if command -v shasum; then sum() { shasum -a 256 "$@"; } ; else sum() { sha256sum "$@"; }; fi fragment=$(< package.json command grep 'version"\|"name"\|"files"\|"main"\|"build' | sum | command head -c8) - echo "key=build-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('packages/**/src/**', '**/.swcrc', '**/.npmrc', '**/tsconfig*.json', '**/pnpm-lock.yaml', '.github/workflows/build.yml') }}-${fragment}" | tee -a $GITHUB_OUTPUT + echo "key=build-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/src/**', '**/.swcrc', '**/.npmrc', '**/tsconfig*.json', '**/pnpm-lock.yaml', '.github/workflows/build.yml') }}-${fragment}" | tee -a $GITHUB_OUTPUT shell: bash - uses: actions/setup-node@v3 diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index c3c478e4..f6d00c0d 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -8,6 +8,7 @@ env: CACHE_DIR: installs CACHE_TEST_DIR: cache-test-e2e MAIN_PACKAGE_PATH: packages/legacy + PANERON_TEST_DIR_NAME: paneron-for-tests jobs: test-e2e: @@ -35,7 +36,7 @@ jobs: run: | if command -v shasum; then sum() { shasum -a 256 "$@"; } ; else sum() { sha256sum "$@"; }; fi fragment=$(< package.json command grep 'version"\|"name"\|"files"\|"main"\|"build' | sum | command head -c8) - echo "key=build-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('packages/**/src/**', '**/.swcrc', '**/.npmrc', '**/tsconfig*.json', '**/pnpm-lock.yaml', '.github/workflows/build.yml') }}-${fragment}" | tee -a $GITHUB_OUTPUT + echo "key=build-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/src/**', '**/.swcrc', '**/.npmrc', '**/tsconfig*.json', '**/pnpm-lock.yaml', '.github/workflows/build.yml') }}-${fragment}" | tee -a $GITHUB_OUTPUT shell: bash - name: Calculate a fragment of cache key @@ -50,7 +51,7 @@ jobs: uses: actions/cache@v3 with: path: ${{ env.CACHE_TEST_DIR }} - key: tests-e2e-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('e2e_tests/**', 'packages/**', '**/.swcrc', '**/.npmrc', 'tsconfig*.json', '**/pnpm-lock.yaml', '.github/workflows/build.yml', '.github/workflows/test-e2e.yml') }}-${{ steps.cache-key.outputs.fragment }} + key: tests-e2e-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('e2e_tests/**', '**/src/**', '**/.swcrc', '**/.npmrc', 'tsconfig*.json', '**/pnpm-lock.yaml', '.github/workflows/build.yml', '.github/workflows/test-e2e.yml') }}-${{ steps.cache-key.outputs.fragment }} - name: Set up Node JS if: steps.test_cache.outputs.cache-hit != 'true' @@ -98,14 +99,14 @@ jobs: with: repository: 'paneron/paneron' fetch-depth: 1 - path: ${{ env.CACHE_DIR }}/paneron + path: ${{ env.CACHE_DIR }}/${{ env.PANERON_TEST_DIR_NAME }} ref: ${{ matrix.paneron_ref }} - name: Build Paneron cache if: ${{ steps.paneron_build_cache.outputs.cache-hit != 'true' && steps.test_cache.outputs.cache-hit != 'true' }} shell: bash run: | - pushd ${{ env.CACHE_DIR }}/paneron + pushd ${{ env.CACHE_DIR }}/${{ env.PANERON_TEST_DIR_NAME }} # Work around node js 17+ OpenSSL if [[ ${{ matrix.node }} = 18* ]]; then \ export NODE_OPTIONS=--openssl-legacy-provider; \ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3106fd5b..6d7cd6aa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,7 +32,7 @@ jobs: run: | if command -v shasum; then sum() { shasum -a 256 "$@"; } ; else sum() { sha256sum "$@"; }; fi fragment=$(< package.json command grep 'version"\|"name"\|"files"\|"main"\|"build' | sum | command head -c8) - echo "key=build-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('packages/**/src/**', '**/.swcrc', '**/.npmrc', '**/tsconfig*.json', '**/pnpm-lock.yaml', '.github/workflows/build.yml') }}-${fragment}" | tee -a $GITHUB_OUTPUT + echo "key=build-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/src/**', '**/.swcrc', '**/.npmrc', '**/tsconfig*.json', '**/pnpm-lock.yaml', '.github/workflows/build.yml') }}-${fragment}" | tee -a $GITHUB_OUTPUT shell: bash - name: Calculate a fragment of cache key @@ -47,7 +47,7 @@ jobs: uses: actions/cache@v3 with: path: ${{ env.CACHE_TEST_DIR }} - key: tests-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('packages/**', '**/.swcrc', '**/.npmrc', '**/tsconfig*.json', '**/pnpm-lock.yaml', '.github/workflows/build.yml', '.github/workflows/test.yml') }}-${{ steps.cache-key.outputs.fragment }} + key: tests-${{ runner.os }}-${{ matrix.node }}-${{ hashFiles('**/src/**', '**/.swcrc', '**/.npmrc', '**/tsconfig*.json', '**/pnpm-lock.yaml', '.github/workflows/build.yml', '.github/workflows/test.yml') }}-${{ steps.cache-key.outputs.fragment }} - uses: actions/setup-node@v3 if: steps.test_cache.outputs.cache-hit != 'true' diff --git a/Makefile b/Makefile index fc96a1f6..c57d5c79 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,5 @@ +PANERON_TEST_DIR_NAME := paneron-for-tests + # https://gist.github.com/klmr/575726c7e05d8780505a # Inspired by # @@ -101,15 +103,15 @@ test-all: test test-e2e test: $(NPM) run test -./paneron/dist/main/main.js: - git clone https://github.com/paneron/paneron +./$(PANERON_TEST_DIR_NAME)/dist/main/main.js: + git clone https://github.com/paneron/paneron "$(PANERON_TEST_DIR_NAME)" git checkout 1ab232b008ba9709c9a80512c0eceaeb525515bf cd paneron && yarn install && yarn compile .PHONY: test-e2e ## Run end-to-end tests -test-e2e: ./paneron/dist/main/main.js $(NPM) run -r test:e2e +test-e2e: ./$(PANERON_TEST_DIR_NAME)/dist/main/main.js .PHONY: audit ## Run security audit for npm packages diff --git a/packages/legacy/codecept.conf.ts b/packages/legacy/codecept.conf.ts index 78912d3a..dadaee18 100644 --- a/packages/legacy/codecept.conf.ts +++ b/packages/legacy/codecept.conf.ts @@ -1,6 +1,4 @@ -// import { setHeadlessWhen, setCommonPlugins } from '@codeceptjs/configure'; -const { setHeadlessWhen, setCommonPlugins } = require('@codeceptjs/configure'); -const { bootstrap } = require('./codeceptjs_presettings.ts'); +import { setHeadlessWhen, setCommonPlugins } from '@codeceptjs/configure'; /** * Turn on headless mode when running with HEADLESS=true environment variable @@ -14,26 +12,22 @@ setHeadlessWhen(process.env.HEADLESS); */ setCommonPlugins(); -export const config = { +export const config: CodeceptJS.MainConfig = { + // tests : './*_test.ts', // tests : '**/__tests__/**/*.[jt]s?(x), **/?(*.)+(spec|test).[tj]s?(x)', tests : './e2e_tests/**/*_test.[tj]s?(x)', + // output : './output', output : './test_outputs', helpers : { Playwright : { url : './paneron/dist/main/main.js', + // url : 'http://localhost', show : true, browser : 'electron' } }, - bootstrap, - include : {}, - name : 'extension-hls', - plugins : { - retryFailedStep : { - enabled : true - }, - screenshotOnFail : { - enabled : true - } - } + include : { + I : './steps_file' + }, + name : 'legacy' } diff --git a/packages/legacy/jest.config.ts b/packages/legacy/jest.config.ts index aad75f57..83798308 100644 --- a/packages/legacy/jest.config.ts +++ b/packages/legacy/jest.config.ts @@ -4,7 +4,35 @@ * https://jestjs.io/docs/configuration */ -export default { +import { pathsToModuleNameMapper } from 'ts-jest'; +import { compilerOptions } from './tsconfig.json'; + +import type { JestConfigWithTsJest } from 'ts-jest'; + +const _pathsToModuleName: Record = pathsToModuleNameMapper( + compilerOptions.paths, + // { prefix : '/' }, +) ?? {}; + +const pathsToModuleName: Record = {} as Record; + +function isString(v : string | string[]): v is string { + return !Array.isArray(v); +} + +const transformPathsForJest = (rhs : string ) : string => { + return rhs.replace(/^\.\//, '/../'); +} + +Object.entries(_pathsToModuleName).forEach(([key, value]) => { + const newValue = isString(value) ? + transformPathsForJest(value) : + value.map(transformPathsForJest); + + pathsToModuleName[key] = newValue; +}) + +const jestConfig: JestConfigWithTsJest = { // All imported modules in your tests should be mocked automatically // automock: false, @@ -30,9 +58,9 @@ export default { coverageDirectory : 'coverage', // An array of regexp pattern strings used to skip coverage collection - // coveragePathIgnorePatterns: [ - // "/node_modules/" - // ], + coveragePathIgnorePatterns : [ + '/dist/' + ], // Indicates which provider should be used to instrument code for coverage // coverageProvider: "babel", @@ -103,10 +131,13 @@ export default { // A map from regular expressions to module names or to arrays of module // names that allow to stub out resources with a single module // moduleNameMapper: {}, + moduleNameMapper : pathsToModuleName, // An array of regexp pattern strings, matched against all module paths // before considered 'visible' to the module loader - // modulePathIgnorePatterns: [], + modulePathIgnorePatterns : [ + '/dist/', + ], // Activates notifications for test results // notify: false, @@ -140,9 +171,11 @@ export default { // rootDir: undefined, // A list of paths to directories that Jest should use to search for files in - // roots: [ - // "" - // ], + roots : [ + '' + ], + + modulePaths : [compilerOptions.baseUrl], // <-- This will be set to 'baseUrl' value // Allows you to use a custom runner instead of Jest's default test runner // runner: "jest-runner", @@ -186,7 +219,7 @@ export default { testPathIgnorePatterns : [ '/node_modules/', '/dist/', - '/paneron/', + '/paneron-for-tests/', ], // The regexp pattern or array of patterns that Jest uses to detect test files @@ -203,7 +236,7 @@ export default { // Use @swc/jest to support nodejs 14.x: transform : { - '^.+\\.(t|j)sx?$' : ['@swc/jest'], + '^.+\\.(t|j)sx?$' : ['@swc/jest', {}], }, // An array of regexp pattern strings that are matched against all source @@ -227,3 +260,5 @@ export default { // Whether to use watchman for file crawling // watchman: true, }; + +export default jestConfig; diff --git a/packages/legacy/package.json b/packages/legacy/package.json index ef733eef..db4b9e47 100644 --- a/packages/legacy/package.json +++ b/packages/legacy/package.json @@ -21,8 +21,8 @@ "typecheck": "tsc -p tsconfig.build.json --noEmit", "build:decl": "tsc -p tsconfig.build.json --emitDeclarationOnly", "build": "run-script-os", - "build:default": "swc src --ignore '*.test.*' --out-dir ${npm_package_dist_dir} --copy-files --extensions '.ts,.tsx,.js,.jsx' && cp package.json ${npm_package_dist_dir}/", - "build:win32": "mkdirp %npm_package_dist_dir% && swc src --out-dir %npm_package_dist_dir% --copy-files --extensions '.ts,.tsx,.js,.jsx' && copy package.json %npm_package_dist_dir%", + "build:default": "swc src --sync --ignore '*.test.*' --out-dir ${npm_package_dist_dir} --copy-files --extensions '.ts,.tsx,.js,.jsx,.json' && cp package.json ${npm_package_dist_dir}/", + "build:win32": "mkdirp %npm_package_dist_dir% && swc src --sync --out-dir %npm_package_dist_dir% --copy-files --extensions '.ts,.tsx,.js,.jsx,.json' && copy package.json %npm_package_dist_dir%", "inject": "run-script-os", "inject:mac": "mkdirp $HOME/'Library/Application Support'/Paneron/plugins/${npm_package_name}; rsync -a ${npm_package_dist_dir}/ $HOME/'Library/Application Support'/Paneron/plugins/${npm_package_name}/", "inject:win32": "mkdirp \"%appdata%\\Paneron\\plugins\\@paneron\\extension-hls\" && xcopy /e /y %npm_package_dist_dir% \"%appdata%\\Paneron\\plugins\\@paneron\\extension-hls\" > nul", @@ -70,17 +70,18 @@ "@riboseinc/paneron-extension-glossarist": "^2.0.7", "@riboseinc/paneron-extension-kit": "^2.0.7", "@swc/cli": "^0.1.57", - "@swc/core": "^1.3.14", - "@swc/jest": "^0.2.23", + "@swc/core": "^1.3.35", + "@swc/helpers": "^0.4.14", + "@swc/jest": "^0.2.24", "@types/jest": "^29.2.2", "@types/node": "18.11.9", "@types/react": "16.14.30", "@types/react-reconciler": "^0.28.0", "@types/three": "^0.146.0", "@types/webpack-env": "^1.18.0", - "@typescript-eslint/eslint-plugin": "^5.42.1", - "@typescript-eslint/parser": "^5.42.1", - "codeceptjs": "^3.3.6", + "@typescript-eslint/eslint-plugin": "^5.52.0", + "@typescript-eslint/parser": "^5.52.0", + "codeceptjs": "^3.4.1", "concurrently": "^7.5.0", "electron": "^24.3.0", "eslint": "^8.27.0", diff --git a/packages/legacy/src/smart/model/editor/commands/data.ts b/packages/legacy/src/smart/model/editor/commands/data.ts index 17fafa0d..ed00e6f6 100644 --- a/packages/legacy/src/smart/model/editor/commands/data.ts +++ b/packages/legacy/src/smart/model/editor/commands/data.ts @@ -64,7 +64,7 @@ export function editImportRegistryCommand( id: string, value: RegistryCombined, refs: MMELReference[] -) { +): ModelAction { const action: ModelAction = { type : 'model', act : 'hybird', @@ -135,7 +135,7 @@ export function editImportDCCommand( id: string, value: EditorDataClass, refs: MMELReference[] -) { +): ModelAction { const action: ModelAction = { type : 'model', act : 'hybird', diff --git a/packages/legacy/src/smart/model/editor/commands/elements.ts b/packages/legacy/src/smart/model/editor/commands/elements.ts index 3900bca2..8d612789 100644 --- a/packages/legacy/src/smart/model/editor/commands/elements.ts +++ b/packages/legacy/src/smart/model/editor/commands/elements.ts @@ -48,7 +48,7 @@ export function editEGateCommand( page: string, update: EditorEGate, edges: MMELEdge[] -) { +): ModelAction { const action: ModelAction = { type : 'model', act : 'hybird', @@ -65,7 +65,7 @@ export function editEGateCommand( * Add a subprocess page to the process * @param id The Process ID */ -export function createSubprocessCommand(id: string) { +export function createSubprocessCommand(id: string): ModelAction { const action: ModelAction = { type : 'model', act : 'hybird', @@ -79,7 +79,7 @@ export function createSubprocessCommand(id: string) { * Remove the subprocess page from the process * @param id The Process ID */ -export function deleteSubprocessCommand(id: string) { +export function deleteSubprocessCommand(id: string): ModelAction { const action: ModelAction = { type : 'model', act : 'hybird', @@ -94,7 +94,7 @@ export function deleteSubprocessCommand(id: string) { * @param id The process ID * @param page The page where the process is removed */ -export function bringoutProcessCommand(id: string, page: string) { +export function bringoutProcessCommand(id: string, page: string): ModelAction { const action: ModelAction = { type : 'model', act : 'hybird', @@ -121,7 +121,7 @@ export function editProcessCommand( notes: MMELNote[], links: MMELLink[], refs: MMELReference[] -) { +): ModelAction { const newProcess: EditorProcess = { ...process, provision : new Set(provisions.map(x => x.id)), diff --git a/packages/legacy/src/smart/model/editor/commands/import.ts b/packages/legacy/src/smart/model/editor/commands/import.ts index 7aad70a1..5afafdf9 100644 --- a/packages/legacy/src/smart/model/editor/commands/import.ts +++ b/packages/legacy/src/smart/model/editor/commands/import.ts @@ -19,7 +19,7 @@ export function importElmCommand( x: number, y: number, page: string -) { +): ModelAction { const action: ModelAction = { type : 'model', act : 'hybird', diff --git a/packages/legacy/src/smart/model/editor/commands/page.ts b/packages/legacy/src/smart/model/editor/commands/page.ts index 048a9152..693a3d24 100644 --- a/packages/legacy/src/smart/model/editor/commands/page.ts +++ b/packages/legacy/src/smart/model/editor/commands/page.ts @@ -13,7 +13,7 @@ import { ModelAction } from '../model'; * @param page The page ID * @param id The edge ID */ -export function removeEdgeCommand(page: string, id: string) { +export function removeEdgeCommand(page: string, id: string): ModelAction { const action: ModelAction = { type : 'model', act : 'pages', @@ -34,7 +34,7 @@ export function newEdgeCommand( page: EditorSubprocess, source: string, target: string -) { +): ModelAction { const newEdge = createEdge(findUniqueID('Edge', page.edges)); newEdge.from = source; newEdge.to = target; @@ -65,7 +65,7 @@ export function dragCommand( model: EditorModel, flowNode: Node, ds: DragStartRecord -) { +): ModelAction { const action: ModelAction = { type : 'model', act : 'pages', diff --git a/packages/legacy/steps_file.ts b/packages/legacy/steps_file.ts new file mode 100644 index 00000000..9fbfe9f1 --- /dev/null +++ b/packages/legacy/steps_file.ts @@ -0,0 +1,10 @@ +// in this file you can append custom step methods to 'I' object + +module.exports = function() { + return actor({ + + // Define custom steps here, use 'this' to access default methods of I. + // It is recommended to place a general 'login' function here. + + }); +} diff --git a/packages/legacy/tsconfig.json b/packages/legacy/tsconfig.json index 4ddb2618..8b9d32eb 100644 --- a/packages/legacy/tsconfig.json +++ b/packages/legacy/tsconfig.json @@ -14,8 +14,24 @@ "declarationMap": true, "jsx": "react", "outDir": "dist", - "allowJs": false + "typeRoots": [ + "../../src/@types", + "./node_modules/@types" + ], + "composite": true, + "module": "esnext", + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "baseUrl": ".", + "paths": { + } }, + "include": [ + "**.json", + "*.ts", + "src" + ], "exclude": [ "node_modules", "dist", @@ -23,6 +39,10 @@ "backup" ], "ts-node": { + "swc": true, + "compilerOptions": { + "module": "CommonJS" + }, "files": true } } diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 00000000..7d48a6c5 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "composite": true, + "target": "esnext", + "module": "commonjs", + "moduleResolution": "node", + "declaration": true, + "declarationMap": true, + "isolatedModules": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "skipLibCheck": true, + "incremental": true, + "experimentalDecorators": true, + "allowJs": false, + "jsx": "react", + "newLine": "lf" + }, + "exclude": [ + "node_modules", + "backup", + "compiled", + "dist" + ] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..0f0d61d1 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "composite": true, + "target": "esnext", + "module": "commonjs", + "moduleResolution": "node", + "declaration": true, + "declarationMap": true, + "isolatedModules": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "strict": true, + "skipLibCheck": true, + "incremental": true, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "allowJs": false, + "jsx": "react", + "newLine": "lf" + }, + "files": [] +}