├── .env.example ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── README.md ├── bottender.config.js ├── index.js ├── jest.config.js ├── package-lock.json ├── package.json ├── point.json ├── src ├── exception.ts ├── index.test.ts ├── index.ts ├── location.ts ├── report.ts └── today.ts └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID= 2 | MESSENGER_ACCESS_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | MESSENGER_VERIFY_TOKEN= 6 | 7 | LINE_ACCESS_TOKEN= 8 | LINE_CHANNEL_SECRET= 9 | 10 | TELEGRAM_ACCESS_TOKEN= 11 | 12 | SLACK_ACCESS_TOKEN= 13 | SLACK_VERIFICATION_TOKEN= 14 | 15 | VIBER_ACCESS_TOKEN= 16 | 17 | MASK_API_PATH= -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:@typescript-eslint/eslint-recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'prettier', 8 | 'prettier/@typescript-eslint', 9 | ], 10 | env: { 11 | node: true, 12 | }, 13 | rules: { 14 | 'prettier/prettier': [ 15 | 'error', 16 | { 17 | trailingComma: 'es5', 18 | singleQuote: true, 19 | }, 20 | ], 21 | '@typescript-eslint/camelcase': 'off', 22 | '@typescript-eslint/no-use-before-define': 'off' 23 | }, 24 | plugins: ['import', 'prettier', '@typescript-eslint'], 25 | overrides: [ 26 | { 27 | files: ['**/*.test.ts'], 28 | env: { 29 | jest: true, 30 | }, 31 | }, 32 | ], 33 | }; 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | 84 | # Gatsby files 85 | .cache/ 86 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 87 | # https://nextjs.org/blog/next-9-1#public-directory-support 88 | # public 89 | 90 | # vuepress build output 91 | .vuepress/dist 92 | 93 | # Serverless directories 94 | .serverless/ 95 | 96 | # FuseBox cache 97 | .fusebox/ 98 | 99 | # DynamoDB Local files 100 | .dynamodb/ 101 | 102 | # TernJS port file 103 | .tern-port 104 | 105 | # TypeScript build output 106 | dist/ 107 | 108 | # Generated by MacOS 109 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MaskHelpBot 2 | A MaskHelpBot from the Chatbot Taiwan community❤️ 3 | 4 | ## Prerequisites 5 | If you want to use the LINE chatbot, Please go to [LINE Developers](https://developers.line.biz/) to register an account, and get `Channel secret` and `Channel access token`, and fill in `.env` (`LINE_CHANNEL_SECRET` and `LINE_ACCESS_TOKEN`) 6 | 7 | ## Installing 8 | The chatbot is built with Bottender. 9 | 10 | First, run `npm install` to install the dependency package. 11 | 12 | Then, run `tsc -w` to compile typescript files to /disc folder. 13 | 14 | At last run `npm start` in production mode or `npm run dev` in development mode 15 | 16 | Finally, you are ready to interact with your bot :) 17 | 18 | Rembmer, you have to manually copy your webhook url (default: `/webhooks/line`) to LINE@ account's setting page 19 | 20 | ## Running the tests 21 | TODO... 22 | 23 | ## TODO 24 | 25 | ## Built With 26 | [Bottender](https://github.com/yoctol/bottender) - A framework for building conversational user interfaces. 27 | 28 | [TypeScript](https://github.com/microsoft/TypeScript) - TypeScript is a superset of JavaScript that compiles to clean JavaScript output. 29 | 30 | ## Contributing 31 | [Eric](https://ericwu.asia/) 32 | 33 | ## Authors 34 | [Eric](https://ericwu.asia/) 35 | -------------------------------------------------------------------------------- /bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | session: { 3 | driver: 'memory', 4 | stores: { 5 | memory: { 6 | maxSize: 500, 7 | }, 8 | file: { 9 | dirname: '.sessions', 10 | }, 11 | redis: { 12 | port: 6379, 13 | host: '127.0.0.1', 14 | password: 'auth', 15 | db: 0, 16 | }, 17 | mongo: { 18 | url: 'mongodb://localhost:27017', 19 | collectionName: 'sessions', 20 | }, 21 | }, 22 | }, 23 | initialState: {}, 24 | channels: { 25 | messenger: { 26 | enabled: false, 27 | path: '/webhooks/messenger', 28 | pageId: process.env.MESSENGER_PAGE_ID, 29 | accessToken: process.env.MESSENGER_ACCESS_TOKEN, 30 | appId: process.env.MESSENGER_APP_ID, 31 | appSecret: process.env.MESSENGER_APP_SECRET, 32 | verifyToken: process.env.MESSENGER_VERIFY_TOKEN, 33 | }, 34 | line: { 35 | enabled: true, 36 | path: '/webhooks/line', 37 | accessToken: process.env.LINE_ACCESS_TOKEN, 38 | channelSecret: process.env.LINE_CHANNEL_SECRET, 39 | }, 40 | telegram: { 41 | enabled: false, 42 | path: '/webhooks/telegram', 43 | accessToken: process.env.TELEGRAM_ACCESS_TOKEN, 44 | }, 45 | slack: { 46 | enabled: false, 47 | path: '/webhooks/slack', 48 | accessToken: process.env.SLACK_ACCESS_TOKEN, 49 | verificationToken: process.env.SLACK_VERIFICATION_TOKEN, 50 | }, 51 | viber: { 52 | enabled: false, 53 | path: '/webhooks/viber', 54 | accessToken: process.env.VIBER_ACCESS_TOKEN, 55 | sender: { 56 | name: 'xxxx', 57 | }, 58 | }, 59 | }, 60 | }; 61 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist').default; 2 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | moduleFileExtensions: ['js'], 4 | transformIgnorePatterns: ['/node_modules/'], 5 | testMatch: [ 6 | '/**/__tests__/**/*.js', 7 | '/**/*.{spec,test}.js', 8 | ], 9 | }; 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chatbot-bot", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "tsc", 7 | "dev": "bottender dev", 8 | "lint": "eslint . --ext=js,ts", 9 | "start": "bottender start", 10 | "test": "jest" 11 | }, 12 | "dependencies": { 13 | "@types/json-query": "^2.2.0", 14 | "axios": "^0.21.1", 15 | "bottender": "^1.4.0", 16 | "csv-parse": "^4.8.5", 17 | "json-query": "^2.2.2", 18 | "prettier": "1.19.1", 19 | "xmlhttprequest": "^1.8.0" 20 | }, 21 | "devDependencies": { 22 | "@types/jest": "^25.1.4", 23 | "@typescript-eslint/eslint-plugin": "^2.25.0", 24 | "@typescript-eslint/parser": "^2.25.0", 25 | "eslint": "^6.8.0", 26 | "eslint-config-prettier": "^6.10.1", 27 | "eslint-plugin-import": "^2.20.2", 28 | "eslint-plugin-prettier": "^3.1.2", 29 | "jest": "^25.1.0", 30 | "prettier": "^2.0.2", 31 | "ts-jest": "^25.2.1", 32 | "typescript": "^3.8.3" 33 | }, 34 | "postinstall": "npm run tsc" 35 | } 36 | -------------------------------------------------------------------------------- /src/exception.ts: -------------------------------------------------------------------------------- 1 | import { LineContext, LineTypes } from 'bottender'; 2 | 3 | export async function ExceptionReply(context: LineContext): Promise { 4 | const bubble: LineTypes.FlexBubble = { 5 | type: 'bubble', 6 | body: { 7 | type: 'box', 8 | layout: 'vertical', 9 | contents: [ 10 | { 11 | type: 'text', 12 | margin: 'sm', 13 | text: '無法理解', 14 | weight: 'bold', 15 | size: 'lg', 16 | }, 17 | { 18 | type: 'text', 19 | margin: 'sm', 20 | wrap: true, 21 | text: '我無法理解您的內容,試著直接傳送位址看看!', 22 | color: '#777777', 23 | size: 'md', 24 | flex: 1, 25 | }, 26 | ], 27 | }, 28 | footer: { 29 | type: 'box', 30 | layout: 'vertical', 31 | contents: [ 32 | { 33 | type: 'button', 34 | action: { 35 | type: 'uri', 36 | label: '傳送位址', 37 | uri: 'line://nv/location', 38 | }, 39 | }, 40 | ], 41 | }, 42 | }; 43 | await context.sendFlex('我無法理解你想表達的內容', bubble); 44 | } 45 | -------------------------------------------------------------------------------- /src/index.test.ts: -------------------------------------------------------------------------------- 1 | import App from '.'; 2 | 3 | describe('index.js', () => { 4 | it('should be defined', () => { 5 | expect(App).toBeDefined(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { LineContext } from 'bottender'; 2 | 3 | import { ExceptionReply } from './exception'; 4 | import { LocationReply } from './location'; 5 | import { TodayReply } from './today'; 6 | 7 | export default async function App(context: LineContext) { 8 | if (context.event.isText) { 9 | if (context.event.text == '今天誰可以買口罩') { 10 | return TodayReply; 11 | } else { 12 | return ExceptionReply; 13 | } 14 | } else if (context.event.isFollow) { 15 | await context.send([ 16 | { 17 | type: 'text', 18 | text: 19 | '感謝您加我好友!這隻機器人主要會協助您找到附近有在販售口罩的藥局', 20 | }, 21 | { 22 | type: 'image', 23 | originalContentUrl: 'https://i.imgur.com/eg5irdx.jpg', 24 | previewImageUrl: 'https://i.imgur.com/eg5irdx.jpg', 25 | }, 26 | ]); 27 | } else if (context.event.isLocation) { 28 | return LocationReply; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/location.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import axios from 'axios'; 3 | import { LineContext, LineTypes } from 'bottender'; 4 | import jsonQuery from 'json-query'; 5 | function locationTemplate(data: any): LineTypes.FlexBubble { 6 | let mask_status_color = '#777777'; 7 | let children_mask_status_color = '#777777'; 8 | if (parseInt(data.adult_number) > 20) { 9 | mask_status_color = '#00ff00'; 10 | } else { 11 | mask_status_color = '#ff0000'; 12 | } 13 | if (parseInt(data.childer_number) > 20) { 14 | children_mask_status_color = '#00ff00'; 15 | } else { 16 | children_mask_status_color = '#ff0000'; 17 | } 18 | return { 19 | type: 'bubble', 20 | body: { 21 | type: 'box', 22 | layout: 'vertical', 23 | contents: [ 24 | { 25 | type: 'text', 26 | text: data.name, 27 | weight: 'bold', 28 | size: 'lg', 29 | }, 30 | { 31 | type: 'text', 32 | margin: 'lg', 33 | text: data.address, 34 | weight: 'bold', 35 | color: '#aaaaaa', 36 | size: 'md', 37 | }, 38 | { 39 | type: 'box', 40 | layout: 'vertical', 41 | margin: 'lg', 42 | contents: [ 43 | { 44 | type: 'box', 45 | layout: 'baseline', 46 | contents: [ 47 | { 48 | type: 'text', 49 | text: '成人口罩剩下', 50 | color: '#aaaaaa', 51 | size: 'md', 52 | flex: 4, 53 | }, 54 | { 55 | type: 'text', 56 | text: data.adult_number, 57 | wrap: true, 58 | color: mask_status_color, 59 | size: 'md', 60 | flex: 2, 61 | }, 62 | ], 63 | }, 64 | ], 65 | }, 66 | { 67 | type: 'box', 68 | layout: 'vertical', 69 | margin: 'lg', 70 | contents: [ 71 | { 72 | type: 'box', 73 | layout: 'baseline', 74 | contents: [ 75 | { 76 | type: 'text', 77 | text: '兒童口罩剩下', 78 | color: '#aaaaaa', 79 | size: 'md', 80 | flex: 4, 81 | }, 82 | { 83 | type: 'text', 84 | text: data.childer_number, 85 | wrap: true, 86 | color: children_mask_status_color, 87 | size: 'md', 88 | flex: 2, 89 | }, 90 | ], 91 | }, 92 | ], 93 | }, 94 | ], 95 | }, 96 | footer: { 97 | type: 'box', 98 | layout: 'vertical', 99 | contents: [ 100 | { 101 | type: 'button', 102 | action: { 103 | type: 'uri', 104 | label: 'Google Map', 105 | uri: 106 | 'https://www.google.com/maps/search/?api=1&query=' + 107 | data.lat + 108 | ',' + 109 | data.lng, 110 | }, 111 | }, 112 | { 113 | type: 'button', 114 | action: { 115 | type: 'uri', 116 | label: '緊急聯繫電話', 117 | uri: 'tel:' + data.phone, 118 | }, 119 | }, 120 | ], 121 | }, 122 | }; 123 | } 124 | 125 | async function getData(lat: string, lng: string) { 126 | const path = 'point.json'; 127 | let fileContent, jsonContent; 128 | 129 | new Promise(function() { 130 | fileContent = fs.readFileSync(path, { encoding: 'utf8' }); 131 | jsonContent = JSON.parse(fileContent); 132 | }); 133 | 134 | return await queryNearbyPoint(jsonContent, lat, lng); 135 | } 136 | 137 | async function queryNearbyPoint(data: any, lat: any, lng: any) { 138 | // Get nearby place 139 | const nearby_point_array: any = []; 140 | 141 | data.forEach( 142 | (element: { 143 | Response_X: any; 144 | Response_Y: any; 145 | lat_min: any; 146 | lat_mix: any; 147 | }) => { 148 | if (distance(lat, lng, element.Response_Y, element.Response_X) <= 5) { 149 | nearby_point_array.push(element); 150 | } 151 | } 152 | ); 153 | const resp_data: any = []; 154 | 155 | const csv: any = await getNHIData(); 156 | nearby_point_array.forEach((element: any) => { 157 | const wanted = jsonQuery('data[code=' + element.醫事機構代碼 + ']', { 158 | data: csv, 159 | }); 160 | if (wanted.value != null) { 161 | const d = wanted.value; 162 | d['lng'] = element.Response_X; 163 | d['lat'] = element.Response_Y; 164 | resp_data.push(d); 165 | } 166 | }); 167 | return resp_data; 168 | } 169 | 170 | function distance(lat1: any, lon1: any, lat2: any, lon2: any) { 171 | const radlat1 = (Math.PI * lat1) / 180; 172 | const radlat2 = (Math.PI * lat2) / 180; 173 | const theta = lon1 - lon2; 174 | const radtheta = (Math.PI * theta) / 180; 175 | let dist = 176 | Math.sin(radlat1) * Math.sin(radlat2) + 177 | Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta); 178 | if (dist > 1) { 179 | dist = 1; 180 | } 181 | dist = Math.acos(dist); 182 | dist = (dist * 180) / Math.PI; 183 | dist = dist * 60 * 1.1515 * 1.609344; 184 | return dist; 185 | } 186 | 187 | async function getNHIData() { 188 | // Get NHI Data 189 | const mask_path = 'https://data.nhi.gov.tw/resource/mask/maskdata.csv'; 190 | const allText: any = await axios.get(mask_path); 191 | const text = allText.data; 192 | const lines = text.split('\n'); 193 | const results = []; 194 | 195 | for (let i = 1; i < lines.length - 1; i++) { 196 | const currentline = lines[i].split(','); 197 | const obj = { 198 | code: currentline[0], 199 | name: currentline[1], 200 | address: currentline[2], 201 | phone: currentline[3], 202 | adult_number: currentline[4], 203 | childer_number: currentline[5], 204 | update: currentline[6], 205 | }; 206 | results.push(obj); 207 | } 208 | return JSON.parse( 209 | JSON.stringify({ 210 | data: results, 211 | }) 212 | ); 213 | } 214 | 215 | export async function LocationReply(context: LineContext): Promise { 216 | const latitude = context.event.message.latitude; 217 | const longitude = context.event.message.longitude; 218 | 219 | const response = await getData(latitude, longitude); 220 | 221 | if (response.length > 0) { 222 | const contents: LineTypes.FlexBubble[] = []; 223 | let carouselNum = 1; 224 | 225 | for (const index in response) { 226 | const placeBubble: LineTypes.FlexBubble = locationTemplate( 227 | response[index] 228 | ); 229 | contents.push(placeBubble); 230 | carouselNum++; 231 | if (carouselNum == 10) { 232 | break; 233 | } 234 | } 235 | 236 | const carousel: LineTypes.FlexCarousel = { 237 | type: 'carousel', 238 | contents: contents, 239 | }; 240 | await context.sendFlex('您傳送位址附近的藥局', carousel); 241 | } else { 242 | await context.sendText('您傳送位址附近沒有可用的藥局'); 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /src/report.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { LineContext, LineTypes } from 'bottender'; 3 | 4 | export async function ReportReply(context: LineContext): Promise { 5 | const data = context.event.postback.data.split('&'); 6 | const id = data[1].split('=')[1]; 7 | const quickReply: LineTypes.QuickReply = { 8 | items: [ 9 | { 10 | type: 'action', 11 | action: { 12 | type: 'postback', 13 | label: '充足', 14 | data: 'report_status&status=AVAILABLE&id=' + id, 15 | }, 16 | }, 17 | { 18 | type: 'action', 19 | action: { 20 | type: 'postback', 21 | label: '短缺', 22 | data: 'report_status&status=SHORTAGE&id=' + id, 23 | }, 24 | }, 25 | ], 26 | }; 27 | await context.send([ 28 | { 29 | type: 'text', 30 | text: '請問該店的口罩使用狀態?', 31 | quickReply, 32 | }, 33 | ]); 34 | } 35 | 36 | async function sendData(status: string, id: any) { 37 | const url = process.env.MASK_API_PATH + '/api/supply/' + id; 38 | const readyData = { 39 | maskStatus: status, 40 | }; 41 | 42 | return await axios.patch(url, readyData); 43 | } 44 | 45 | export async function ReportStatusReply(context: LineContext): Promise { 46 | const data = context.event.postback.data.split('&'); 47 | const status = data[1].split('=')[1]; 48 | const id = data[2].split('=')[1]; 49 | await sendData(status, id); 50 | await context.send([ 51 | { 52 | type: 'text', 53 | text: '感謝您的回報!口罩夠用就好!請不要囤貨喔!', 54 | }, 55 | ]); 56 | } 57 | -------------------------------------------------------------------------------- /src/today.ts: -------------------------------------------------------------------------------- 1 | import { LineContext } from 'bottender'; 2 | 3 | export async function TodayReply(context: LineContext) { 4 | const d = new Date(); 5 | const n = d.getDay(); 6 | let reply_text = ''; 7 | if (n == 2 || n == 4 || n == 6) { 8 | reply_text = '身分證末一碼是『偶數』的才可以買口罩。'; 9 | } else if (n == 1 || n == 3 || n == 5) { 10 | reply_text = '身分證末一碼是『奇數』的才可以買口罩。'; 11 | } else { 12 | reply_text = '今天大家都可以買口罩!'; 13 | } 14 | await context.sendText(reply_text); 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | // "incremental": true, /* Enable incremental compilation */ 5 | "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, 6 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, 7 | // "lib": [], /* Specify library files to be included in the compilation. */ 8 | // "allowJs": true, /* Allow javascript files to be compiled. */ 9 | // "checkJs": true, /* Report errors in .js files. */ 10 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 11 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 12 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 13 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 14 | // "outFile": "./", /* Concatenate and emit output to single file. */ 15 | "outDir": "./dist" /* Redirect output structure to the directory. */, 16 | "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, 17 | // "composite": true, /* Enable project compilation */ 18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 19 | // "removeComments": true, /* Do not emit comments to output. */ 20 | // "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | "strict": true /* Enable all strict type-checking options. */, 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 30 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | /* Module Resolution Options */ 42 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 52 | 53 | /* Source Map Options */ 54 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 55 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 56 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 57 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 58 | 59 | /* Experimental Options */ 60 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 61 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 62 | 63 | /* Advanced Options */ 64 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, 65 | "resolveJsonModule": true 66 | } 67 | } 68 | --------------------------------------------------------------------------------