├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .prettierrc ├── README.md ├── __tests__ ├── async.test.js ├── decorator.test.js ├── eslintJs.test.js ├── import.test.js ├── index.test.js ├── nullish.test.js ├── optionalChaining.test.js └── suite │ ├── Component.tsx │ ├── fix.jsx │ ├── index.ts │ ├── sub │ └── Component.tsx │ └── test.jsx ├── package.json ├── src ├── babel-decorator-plugin.ts ├── eslintJs.ts ├── index.ts ├── prettierJs.ts ├── ts2js.ts └── typing.ts └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | __tests__/suite 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [require.resolve('@umijs/fabric/dist/eslint')], 3 | globals: { 4 | ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION: true, 5 | page: true, 6 | REACT_APP_ENV: true, 7 | }, 8 | parserOptions: { 9 | tsconfigRootDir: __dirname, 10 | project: './tsconfig.json', 11 | /** 12 | * parserOptions.createDefaultProgram 13 | * Default .false 14 | * This option allows you to request that when the setting is specified, 15 | * files will be allowed when not included in the projects defined by the provided files. 16 | * Using this option will incur significant performance costs. 17 | * This option is primarily included for backwards-compatibility. 18 | * See the project section above for more information.projecttsconfig.json 19 | */ 20 | createDefaultProgram: true, 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib/ 3 | __tests__/tmp* 4 | yarn.lock 5 | yarn-error.log 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | __tests__ 2 | src 3 | node_modules 4 | .gitignore 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "printWidth": 100, 5 | "proseWrap": "never", 6 | "overrides": [ 7 | { 8 | "files": ".prettierrc", 9 | "options": { 10 | "parser": "json" 11 | } 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sylvanas 2 | 3 | A tool to convert TypeScript to JavaScript with human-like code style. 4 | 5 | ## How to use 6 | 7 | ```bash 8 | npm install --save-dev sylvanas 9 | ``` 10 | 11 | ### sylvanas(files: string[], option?: Option) 12 | 13 | ```js 14 | const sylvanas = require('sylvanas'); 15 | 16 | const files = glob.sync('**/*.@(ts|tsx)'); 17 | 18 | const fileList = sylvanas(files); 19 | 20 | fileList.forEach(({ data }) => { 21 | console.log('Trans:', data); 22 | }); 23 | ``` 24 | 25 | ### Option 26 | 27 | #### cwd - string 28 | 29 | The current working directory in which to search. Defaults to `process.cwd()`. 30 | 31 | #### action - `none` | `write` | `overwrite` 32 | 33 | Default `none`. Set what will Sylvanas do with files: 34 | 35 | - `write`: Write new file with name of suffix `.js` or `.jsx`. 36 | - `overwrite`: Like `write` but will remove origin files. 37 | 38 | #### outDir - string 39 | 40 | Set the write file folder. Defaults to `cwd`. 41 | 42 | #### decoratorsBeforeExport - boolean 43 | 44 | Same as [babel decoratorsbeforeexport](https://babeljs.io/docs/en/babel-plugin-proposal-decorators#decoratorsbeforeexport). 45 | -------------------------------------------------------------------------------- /__tests__/async.test.js: -------------------------------------------------------------------------------- 1 | const ts2js = require('../lib/ts2js').default; 2 | 3 | function parse(text, option) { 4 | const parsed = ts2js([{ data: text.trim() }], option)[0].data; 5 | return parsed; 6 | } 7 | 8 | describe('async', () => { 9 | it('class', () => { 10 | const text = parse( 11 | ` 12 | async function test() { 13 | const a = await 233; 14 | } 15 | `, 16 | ); 17 | expect(text.includes('async')).toBeTruthy(); 18 | expect(text.includes('await')).toBeTruthy(); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /__tests__/decorator.test.js: -------------------------------------------------------------------------------- 1 | const ts2js = require('../lib/ts2js').default; 2 | 3 | function parse(text, option) { 4 | const parsed = ts2js([{ data: text.trim() }], option)[0].data; 5 | return parsed; 6 | } 7 | 8 | describe('decorator', () => { 9 | it('class', () => { 10 | const text = parse( 11 | ` 12 | // With Class 13 | @withClass 14 | class A {} 15 | 16 | @withClassVar('Light') 17 | class B {} 18 | `, 19 | ); 20 | expect(text.includes('@withClass')).toBeTruthy(); 21 | expect(text.includes("@withClassVar('Light')")).toBeTruthy(); 22 | }); 23 | 24 | it('before class', () => { 25 | const text = parse( 26 | ` 27 | // With Class 28 | @withClass class A {} 29 | 30 | @withClassVar('Light') class B {} 31 | `, 32 | ); 33 | expect(text.includes('@withClass')).toBeTruthy(); 34 | expect(text.includes("@withClassVar('Light')")).toBeTruthy(); 35 | }); 36 | 37 | it('class props', () => { 38 | const text = parse( 39 | ` 40 | // With Class Props 41 | class A { 42 | @withProp 43 | prop1 = 'Bamboo'; 44 | 45 | @withPropVar('Light') 46 | prop2 = 'Bamboo'; 47 | 48 | @withFunc 49 | func1() {} 50 | 51 | @withFuncVar('Light') 52 | func2() {} 53 | } 54 | `, 55 | ); 56 | 57 | expect(text.includes('@withProp')).toBeTruthy(); 58 | expect(text.includes("@withPropVar('Light')")).toBeTruthy(); 59 | expect(text.includes('@withFunc')).toBeTruthy(); 60 | expect(text.includes("@withFuncVar('Light')")).toBeTruthy(); 61 | }); 62 | 63 | describe('before export', () => { 64 | it('true', () => { 65 | const text = parse( 66 | ` 67 | @decorator 68 | export class Foo {} 69 | `, 70 | { 71 | decoratorsBeforeExport: true, 72 | }, 73 | ); 74 | 75 | expect(text.includes('@decorator')).toBeTruthy(); 76 | }); 77 | 78 | it('false', () => { 79 | const text = parse( 80 | ` 81 | export @decorator class Foo {} 82 | `, 83 | { 84 | decoratorsBeforeExport: false, 85 | }, 86 | ); 87 | 88 | expect(text.includes('@decorator')).toBeTruthy(); 89 | }); 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /__tests__/eslintJs.test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const { lintAndFix } = require('../lib/eslintJs'); 4 | const { parseText } = require('../lib/index'); 5 | 6 | describe('lintAndFix', () => { 7 | const content = fs.readFileSync(path.resolve(__dirname, 'suite/test.jsx'), 'utf-8'); 8 | const fix = fs.readFileSync(path.resolve(__dirname, 'suite/fix.jsx'), 'utf-8'); 9 | 10 | it('basic', () => { 11 | const fixContent = lintAndFix(content, 'index.jsx'); 12 | expect(fixContent).toEqual(fix); 13 | }); 14 | 15 | it('parseText', () => { 16 | const fixContent = parseText(content, { 17 | filename: 'index.jsx', 18 | }); 19 | expect(fixContent).toEqual(fix); 20 | }); 21 | 22 | it('parseText no filename', () => { 23 | const fixContent = parseText(content); 24 | expect(fixContent).not.toEqual(fix); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /__tests__/import.test.js: -------------------------------------------------------------------------------- 1 | const ts2js = require('../lib/ts2js').default; 2 | 3 | function parse(text, option) { 4 | const parsed = ts2js([{ data: text.trim() }], option)[0].data; 5 | return parsed; 6 | } 7 | 8 | describe('import', () => { 9 | it('React', () => { 10 | const text = parse( 11 | ` 12 | const OtherComponent = React.lazy(() => import('./OtherComponent')); 13 | 14 | function MyComponent() { 15 | return ( 16 |
17 | 18 |
19 | ); 20 | } 21 | `, 22 | ); 23 | expect(text.includes('React.lazy')).toBeTruthy(); 24 | expect(text.includes("import('./OtherComponent')")).toBeTruthy(); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /__tests__/index.test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra'); 2 | const glob = require('glob'); 3 | const path = require('path'); 4 | const sylvanas = require('../lib/index'); 5 | 6 | const cwd = path.resolve(__dirname, 'suite'); 7 | 8 | describe('sylvanas', () => { 9 | const files = glob.sync('**/*.@(ts|tsx)', { 10 | cwd, 11 | }); 12 | 13 | it('basic', () => { 14 | const fileList = sylvanas(files, { 15 | cwd, 16 | }); 17 | 18 | expect(fileList.length).toBe(3); 19 | }); 20 | 21 | describe('write', () => { 22 | const tmpSourcePath = path.resolve(__dirname, 'tmpSrc'); 23 | const tmpTargetPath = path.resolve(__dirname, 'tmpTgt'); 24 | 25 | beforeEach(() => { 26 | fs.removeSync(tmpSourcePath); 27 | fs.removeSync(tmpTargetPath); 28 | 29 | fs.copySync(cwd, tmpSourcePath); 30 | }); 31 | 32 | afterEach(() => { 33 | fs.removeSync(tmpSourcePath); 34 | fs.removeSync(tmpTargetPath); 35 | }); 36 | 37 | it('different folder', () => { 38 | sylvanas(files, { 39 | cwd: tmpSourcePath, 40 | outDir: tmpTargetPath, 41 | action: 'write', 42 | }); 43 | 44 | expect( 45 | glob.sync('**/*.@(ts|tsx|js|jsx)', { 46 | cwd: tmpTargetPath, 47 | }).length, 48 | ).toBe(3); 49 | }); 50 | 51 | it('same folder', () => { 52 | sylvanas(files, { 53 | cwd: tmpSourcePath, 54 | action: 'write', 55 | }); 56 | 57 | expect( 58 | glob.sync('**/*.@(ts|tsx|js|jsx)', { 59 | cwd: tmpSourcePath, 60 | }).length, 61 | ).toBe(8); 62 | }); 63 | 64 | it('overwrite folder', () => { 65 | sylvanas(files, { 66 | cwd: tmpSourcePath, 67 | action: 'overwrite', 68 | }); 69 | 70 | expect( 71 | glob.sync('**/*.@(ts|tsx|js|jsx)', { 72 | cwd: tmpSourcePath, 73 | }).length, 74 | ).toBe(5); 75 | }); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /__tests__/nullish.test.js: -------------------------------------------------------------------------------- 1 | const ts2js = require('../lib/ts2js').default; 2 | 3 | function parse(text, option) { 4 | const parsed = ts2js([{ data: text.trim() }], option)[0].data; 5 | return parsed; 6 | } 7 | 8 | describe('Nullish', () => { 9 | it('work', () => { 10 | const source = ` 11 | interface Config { 12 | light?: number; 13 | } 14 | function my(config: Config) { 15 | return config.light ?? 2333; 16 | } 17 | `.trim(); 18 | const text = parse(source); 19 | const cells = text.split(/[\r\n]+/).map((line) => line.trim()); 20 | expect(cells).toEqual(['function my(config) {', 'return config.light ?? 2333;', '}']); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /__tests__/optionalChaining.test.js: -------------------------------------------------------------------------------- 1 | const ts2js = require('../lib/ts2js').default; 2 | 3 | function parse(text, option) { 4 | const parsed = ts2js([{ data: text.trim() }], option)[0].data; 5 | return parsed; 6 | } 7 | 8 | describe('optional chaining', () => { 9 | it('class', () => { 10 | const source = ` 11 | const getRowByKey = (key: string, newData?: TableFormDateType[]) => (newData || data)?.filter((item) => item.key === key)[0]; 12 | `.trim(); 13 | const text = parse(source); 14 | expect(text).toEqual( 15 | `const getRowByKey = (key, newData) => (newData || data)?.filter(item => item.key === key)[0];`, 16 | ); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /__tests__/suite/Component.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface ComponentProps { 4 | title?: string; 5 | children?: React.ReactNode; 6 | } 7 | 8 | interface ComponentState { 9 | keep: boolean; 10 | } 11 | 12 | class Component extends React.Component { 13 | state = { 14 | keep: false, 15 | }; 16 | 17 | render() { 18 | const { title, children } = this.props; 19 | return ( 20 |
21 | {title &&

{title}

} 22 | {children} 23 |
24 | ); 25 | } 26 | } 27 | 28 | export default Component; 29 | -------------------------------------------------------------------------------- /__tests__/suite/fix.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * title: test 3 | * title.zh-CN: 测试 4 | * desc: test css in dependencies,[Link](https://d.umijs.org) 5 | * desc.zh-CN: 测试依赖中的 CSS,[链接](https://d.umijs.org) 6 | */ 7 | import React from 'react'; 8 | import katex from 'katex'; 9 | 10 | export default () =>

Hello {typeof katex}!

; 11 | -------------------------------------------------------------------------------- /__tests__/suite/index.ts: -------------------------------------------------------------------------------- 1 | import Component from './Component'; 2 | import Component2 from './sub/Component'; 3 | 4 | export function sum(...args: number[]) { 5 | let total: number = 0; 6 | args.forEach((num) => { 7 | total += num; 8 | }); 9 | 10 | return total; 11 | } 12 | 13 | export default { Component, Component2 }; 14 | -------------------------------------------------------------------------------- /__tests__/suite/sub/Component.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | interface ComponentProps { 4 | title?: string; 5 | children?: React.ReactNode; 6 | } 7 | 8 | const Component: React.FunctionComponent = ({ title, children }) => { 9 | return ( 10 |
11 | {title &&

{title}

} 12 | {children} 13 |
14 | ); 15 | }; 16 | 17 | export default Component; 18 | -------------------------------------------------------------------------------- /__tests__/suite/test.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * title: test 3 | * title.zh-CN: 测试 4 | * desc: test css in dependencies,[Link](https://d.umijs.org) 5 | * desc.zh-CN: 测试依赖中的 CSS,[链接](https://d.umijs.org) 6 | */ 7 | import React from 'react'; 8 | import katex from 'katex'; 9 | export default () =>

Hello {typeof katex}!

; 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sylvanas", 3 | "version": "0.6.1", 4 | "description": "Covert TS to JS", 5 | "repository": { 6 | "url": "https://github.com/umijs/sylvanas.git" 7 | }, 8 | "license": "MIT", 9 | "author": "zombiej", 10 | "main": "lib/index.js", 11 | "scripts": { 12 | "build": "tsc", 13 | "prepublishOnly": "rm -rf lib && npm run build && np --no-cleanup --yolo --no-publish --any-branch", 14 | "prettier": "prettier --write \"**/**.{js,jsx,tsx,ts,less,md,json}\"", 15 | "test": "npm run build && jest" 16 | }, 17 | "jest": { 18 | "moduleFileExtensions": [ 19 | "js", 20 | "json" 21 | ], 22 | "testMatch": [ 23 | "**/__tests__/**/*.test.js" 24 | ] 25 | }, 26 | "dependencies": { 27 | "@babel/core": "^7.9.0", 28 | "@babel/plugin-syntax-decorators": "^7.8.3", 29 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 30 | "@babel/plugin-transform-typescript": "^7.9.4", 31 | "@types/prettier": "^1.16.4", 32 | "@umijs/fabric": "^2.2.2", 33 | "eslint": "^7.7.0", 34 | "fs-extra": "^8.0.1", 35 | "import-fresh": "^3.1.0", 36 | "prettier": "^2.1.1" 37 | }, 38 | "devDependencies": { 39 | "@types/eslint": "^7.2.2", 40 | "@types/fs-extra": "^7.0.0", 41 | "@types/jest": "^26.0.10", 42 | "@types/node": "^12.0.3", 43 | "@types/react": "^16.8.19", 44 | "glob": "^7.1.4", 45 | "jest": "^24.8.0", 46 | "np": "^6.5.0", 47 | "react": "^16.8.6", 48 | "typescript": "^3.5.1" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/babel-decorator-plugin.ts: -------------------------------------------------------------------------------- 1 | import syntaxDecorators from '@babel/plugin-syntax-decorators'; 2 | 3 | export default () => { 4 | return { 5 | name: 'sylvanas-decorators', 6 | inherits: syntaxDecorators, 7 | visitor: {}, 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /src/eslintJs.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import { CLIEngine } from 'eslint'; 3 | import { FileEntity } from './typing'; 4 | 5 | const importCache = require('import-fresh'); 6 | 7 | const engine = new CLIEngine({ 8 | useEslintrc: false, 9 | fix: true, 10 | baseConfig: importCache(path.resolve(__dirname, '../.eslintrc.js')), 11 | resolvePluginsRelativeTo: path.resolve(__dirname, '..'), 12 | }); 13 | 14 | export const lintAndFix: (content: string, filename?: string) => string = (content, filename) => { 15 | const report = engine.executeOnText(content, filename); 16 | 17 | if (report.results[0] && report.results[0].output) { 18 | return report.results[0].output; 19 | } 20 | return content; 21 | }; 22 | 23 | function eslintJS(jsFiles: FileEntity[]) { 24 | const lintFiles: FileEntity[] = jsFiles.map((entity: FileEntity) => { 25 | let output: string = entity.data; 26 | try { 27 | output = lintAndFix(entity.data, entity.sourceFilePath); 28 | } catch (e) { 29 | console.error(e); 30 | } 31 | return { 32 | ...entity, 33 | data: output, 34 | }; 35 | }); 36 | 37 | return lintFiles; 38 | } 39 | 40 | export default eslintJS; 41 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as fs from 'fs-extra'; 3 | 4 | import ts2js from './ts2js'; 5 | import eslintJs from './eslintJs'; 6 | import prettierJS from './prettierJs'; 7 | import { FileEntity, BabelOption, Option, Action } from './typing'; 8 | 9 | function parse(fileList: FileEntity[], option: BabelOption = {}) { 10 | // Get js from ts 11 | const jsFiles = ts2js(fileList, option); 12 | 13 | // eslint 14 | const lintFiles = eslintJs(jsFiles); 15 | 16 | // prettier 17 | const prettierFiles = prettierJS(lintFiles); 18 | 19 | return prettierFiles; 20 | } 21 | 22 | function sylvanas(files: string[], option: Option) { 23 | const cwd = option.cwd || process.cwd(); 24 | const outDir = option.outDir || cwd; 25 | const action: Action = option.action || 'none'; 26 | 27 | const fileList: FileEntity[] = files.map( 28 | (file): FileEntity => { 29 | const filePath = path.resolve(cwd, file); 30 | const targetFilePath = path.resolve( 31 | outDir, 32 | file.replace(/\.ts$/, '.js').replace(/\.tsx$/, '.jsx'), 33 | ); 34 | 35 | return { 36 | sourceFilePath: filePath, 37 | targetFilePath, 38 | data: fs.readFileSync(filePath, 'utf8'), 39 | }; 40 | }, 41 | ); 42 | 43 | const parsedFileList = parse(fileList, option); 44 | 45 | if (action === 'write' || action === 'overwrite') { 46 | parsedFileList.forEach(({ sourceFilePath, targetFilePath, data }) => { 47 | fs.ensureFileSync(targetFilePath); 48 | fs.writeFileSync(targetFilePath, data); 49 | 50 | if (action === 'overwrite') { 51 | fs.unlinkSync(sourceFilePath); 52 | } 53 | }); 54 | } 55 | 56 | return parsedFileList; 57 | } 58 | 59 | sylvanas.parseText = function parseText(text: string, option: BabelOption = {}): string { 60 | const result = parse( 61 | [ 62 | { 63 | sourceFilePath: option.filename, 64 | data: text, 65 | }, 66 | ], 67 | option, 68 | ); 69 | 70 | return result[0].data; 71 | }; 72 | 73 | export = sylvanas; 74 | -------------------------------------------------------------------------------- /src/prettierJs.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as fs from 'fs'; 3 | import { format, Options } from 'prettier'; 4 | import { FileEntity } from './typing'; 5 | 6 | function prettierJS(jsFiles: FileEntity[]): FileEntity[] { 7 | const str = fs.readFileSync(path.resolve(__dirname, '../.prettierrc'), 'utf8'); 8 | const prettierOption: Options = JSON.parse(str); 9 | prettierOption.parser = 'babel'; 10 | 11 | return jsFiles.map((entity) => { 12 | let { data } = entity; 13 | try { 14 | data = format(entity.data, prettierOption); 15 | } catch (e) { 16 | console.error('error', e); 17 | } 18 | return { 19 | ...entity, 20 | data, 21 | }; 22 | }); 23 | } 24 | 25 | export default prettierJS; 26 | -------------------------------------------------------------------------------- /src/ts2js.ts: -------------------------------------------------------------------------------- 1 | import { transformSync } from '@babel/core'; 2 | import { FileEntity, BabelOption } from './typing'; 3 | import decoratorPlugin from './babel-decorator-plugin'; 4 | 5 | function ts2js(fileList: FileEntity[], option: BabelOption = {}): FileEntity[] { 6 | const jsFiles: FileEntity[] = fileList.map( 7 | (entity): FileEntity => { 8 | const { code } = transformSync(entity.data, { 9 | plugins: [ 10 | [ 11 | decoratorPlugin, 12 | { 13 | decoratorsBeforeExport: !!option.decoratorsBeforeExport, 14 | }, 15 | ], 16 | [require.resolve('@babel/plugin-syntax-dynamic-import')], 17 | [ 18 | require.resolve('@babel/plugin-transform-typescript'), 19 | { 20 | isTSX: true, 21 | }, 22 | ], 23 | ], 24 | }); 25 | 26 | return { 27 | ...entity, 28 | data: code, 29 | }; 30 | }, 31 | ); 32 | 33 | return jsFiles; 34 | } 35 | 36 | export default ts2js; 37 | -------------------------------------------------------------------------------- /src/typing.ts: -------------------------------------------------------------------------------- 1 | export type Action = 'none' | 'write' | 'overwrite'; 2 | 3 | export interface BabelOption { 4 | decoratorsBeforeExport?: boolean; 5 | filename?: string; 6 | } 7 | 8 | export interface Option extends BabelOption { 9 | outDir?: string; 10 | cwd?: string; 11 | action?: Action; 12 | } 13 | 14 | export interface FileEntity { 15 | sourceFilePath: string; 16 | targetFilePath?: string; 17 | data: string; 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "outDir": "lib", 5 | "module": "CommonJS", 6 | "target": "esnext", 7 | "lib": ["esnext"], 8 | "sourceMap": false, 9 | "baseUrl": ".", 10 | "allowSyntheticDefaultImports": true, 11 | "moduleResolution": "node", 12 | "forceConsistentCasingInFileNames": true, 13 | "noImplicitReturns": true, 14 | "suppressImplicitAnyIndexErrors": true, 15 | "noUnusedLocals": true, 16 | "experimentalDecorators": true, 17 | "rootDir": "src", 18 | "paths": { 19 | "@/*": ["./src/*"] 20 | } 21 | }, 22 | "include": ["src/**/*"] 23 | } 24 | --------------------------------------------------------------------------------