├── .prettierignore
├── .env.sample
├── src
├── index.ts
├── interfaces
│ ├── gist-file.ts
│ └── gist-commit-item.ts
├── routes
│ ├── email-route.ts
│ └── gist-route.ts
├── constants
│ └── gist-constants.ts
├── config
│ └── index.ts
├── app.ts
├── services
│ └── gist-service.ts
├── controllers
│ ├── email-controller.ts
│ └── gist-controller.ts
├── utils
│ └── send-email.ts
└── styles
│ └── email-styles.ts
├── .prettierrc.json
├── compose.yaml
├── eslint.config.mjs
├── .gitignore
├── README.md
├── LICENSE
├── package.json
└── tsconfig.json
/.prettierignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 |
4 | *.config.js
--------------------------------------------------------------------------------
/.env.sample:
--------------------------------------------------------------------------------
1 | CLIENT_URLS=http://localhost:5173,http://localhost:5174
2 | MAIL_PASSWORD=my-app-password
3 | MAIL_USERNAME=mail@example.com
4 | MAIL_SERVICE=gmail
5 | PORT=5000
6 | NODE_ENV=dev
7 | GITHUB_TOKEN=a-gh-token
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import app from './app';
2 | import { config } from './config';
3 |
4 | app.listen(config.server.port, () => {
5 | console.log(`Server is running on http://localhost:${config.server.port}`);
6 | });
7 |
--------------------------------------------------------------------------------
/src/interfaces/gist-file.ts:
--------------------------------------------------------------------------------
1 | export interface IGistFile {
2 | filename: string;
3 | type: string;
4 | language: string;
5 | raw_url: string;
6 | size: number;
7 | truncated: boolean;
8 | content?: string;
9 | }
10 |
--------------------------------------------------------------------------------
/src/routes/email-route.ts:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import { send } from '../controllers/email-controller';
3 |
4 | const emailRouter = express.Router();
5 |
6 | emailRouter.post('/send', send);
7 |
8 | export { emailRouter };
9 |
--------------------------------------------------------------------------------
/src/constants/gist-constants.ts:
--------------------------------------------------------------------------------
1 | import { config } from '../config';
2 |
3 | export const gistsBaseUrl = 'https://api.github.com/gists';
4 |
5 | export const headers = {
6 | 'X-GitHub-Api-Version': '2022-11-28',
7 | Authorization: 'Bearer ' + config.api.github,
8 | };
9 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "semi": true,
3 | "singleQuote": true,
4 | "tabWidth": 2,
5 | "useTabs": false,
6 | "printWidth": 100,
7 | "trailingComma": "all",
8 | "bracketSpacing": true,
9 | "arrowParens": "always",
10 | "endOfLine": "auto"
11 | }
--------------------------------------------------------------------------------
/src/interfaces/gist-commit-item.ts:
--------------------------------------------------------------------------------
1 | export interface IGistCommitItem {
2 | user: unknown;
3 | version: string;
4 | commited_at: string;
5 | change_status: {
6 | total: number;
7 | additions: number;
8 | deletions: number;
9 | };
10 | url: string;
11 | }
12 |
--------------------------------------------------------------------------------
/compose.yaml:
--------------------------------------------------------------------------------
1 | services:
2 | drawdb-server:
3 | image: node:20-alpine
4 | container_name: drawdb-server
5 | ports:
6 | - ${PORT:-5000}:${PORT:-5000}
7 | working_dir: /var/www/html
8 | volumes:
9 | - ./:/var/www/html
10 | command: sh -c "npm install && npm run dev"
11 | networks:
12 | - default
13 | env_file:
14 | - .env
15 |
16 | networks:
17 | default:
18 | driver: bridge
19 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import js from "@eslint/js";
2 | import globals from "globals";
3 | import tseslint from "typescript-eslint";
4 | import { defineConfig } from "eslint/config";
5 |
6 |
7 | export default defineConfig([
8 | { files: ["**/*.{js,mjs,cjs,ts}"], plugins: { js }, extends: ["js/recommended"] },
9 | { files: ["**/*.{js,mjs,cjs,ts}"], languageOptions: { globals: globals.node } },
10 | tseslint.configs.recommended,
11 | ]);
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 | /dist
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | test.html
27 | .env
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a simple server that serves as the backend for drawDB. It has 2 functions:
2 |
3 | 1. Send bug reports via email
4 | 2. Handle interfacing with the GitHub REST API
5 |
6 | ### Getting Started
7 |
8 | Set up the environment variables by following `.env.sample`
9 |
10 | ```bash
11 | git clone https://github.com/drawdb-io/drawdb-server.git
12 | cd drawdb-server
13 | npm install
14 | npm start
15 | ```
16 |
17 | ### Docker Compose run in dev
18 |
19 | ```bash
20 | docker compose up -d
21 | ```
--------------------------------------------------------------------------------
/src/config/index.ts:
--------------------------------------------------------------------------------
1 | import { config as dotenvConfig } from 'dotenv';
2 |
3 | dotenvConfig();
4 |
5 | export const config = {
6 | dev: process.env.NODE_ENV === 'dev',
7 | api: {
8 | github: process.env.GITHUB_TOKEN,
9 | },
10 | server: {
11 | port: process.env.PORT || 5000,
12 | allowedOrigins: process.env.CLIENT_URLS ? process.env.CLIENT_URLS.split(',') : [],
13 | },
14 | mail: {
15 | service: process.env.MAIL_SERVICE || 'gmail',
16 | username: process.env.MAIL_USERNAME || '',
17 | password: process.env.MAIL_PASSWORD || '',
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/src/routes/gist-route.ts:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import {
3 | create,
4 | del,
5 | get,
6 | getCommits,
7 | update,
8 | getRevision,
9 | getRevisionsForFile,
10 | } from '../controllers/gist-controller';
11 |
12 | const gistRouter = express.Router();
13 |
14 | gistRouter.post('/', create);
15 | gistRouter.get('/:id', get);
16 | gistRouter.delete('/:id', del);
17 | gistRouter.patch('/:id', update);
18 | gistRouter.get('/:id/commits', getCommits);
19 | gistRouter.get('/:id/:sha', getRevision);
20 | gistRouter.get('/:id/file-versions/:file', getRevisionsForFile);
21 |
22 | export { gistRouter };
23 |
--------------------------------------------------------------------------------
/src/app.ts:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import cors from 'cors';
3 | import { emailRouter } from './routes/email-route';
4 | import { gistRouter } from './routes/gist-route';
5 | import { config } from './config';
6 |
7 | const app = express();
8 |
9 | app.use(express.json());
10 | app.use(
11 | cors({
12 | origin: config.dev
13 | ? '*'
14 | : (origin, callback) => {
15 | if (origin && config.server.allowedOrigins.indexOf(origin) !== -1) {
16 | callback(null, true);
17 | } else {
18 | callback(null, false);
19 | }
20 | },
21 | }),
22 | );
23 |
24 | app.get('/', (req, res) => {
25 | res.send('Hello');
26 | });
27 |
28 | app.use('/email', emailRouter);
29 | app.use('/gists', gistRouter);
30 |
31 | export default app;
32 |
--------------------------------------------------------------------------------
/src/services/gist-service.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { gistsBaseUrl, headers } from '../constants/gist-constants';
3 |
4 | export const GistService = {
5 | deleteGist: async (gistId: string) => {
6 | await axios.delete(`${gistsBaseUrl}/${gistId}`, {
7 | headers,
8 | });
9 | },
10 |
11 | getCommits: async (gistId: string, perPage?: number, page?: number) => {
12 | const { data } = await axios.get(`${gistsBaseUrl}/${gistId}/commits`, {
13 | headers,
14 | params: {
15 | per_page: perPage,
16 | page,
17 | },
18 | });
19 |
20 | return data;
21 | },
22 |
23 | getCommit: async (gistId: string, sha: string) => {
24 | const { data } = await axios.get(`${gistsBaseUrl}/${gistId}/${sha}`, {
25 | headers,
26 | });
27 |
28 | return data;
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/src/controllers/email-controller.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'express';
2 | import { sendEmail } from '../utils/send-email';
3 | import { config } from '../config';
4 | import { emailStyles } from '../styles/email-styles';
5 |
6 | async function send(req: Request, res: Response) {
7 | const { subject, message, attachments } = req.body;
8 |
9 | if (!req.body || !subject || !message) {
10 | res.status(400).json({
11 | success: false,
12 | message: 'Incorrect body',
13 | });
14 | }
15 |
16 | try {
17 | await sendEmail(
18 | subject,
19 | `
${emailStyles}${message}`,
20 | config.mail.username,
21 | config.mail.username,
22 | attachments,
23 | );
24 | res.status(200).json({
25 | success: true,
26 | message: `Email sent to ${config.mail.username}`,
27 | });
28 | } catch {
29 | res.status(500).json({
30 | success: false,
31 | message: 'Something went wrong.',
32 | });
33 | }
34 | }
35 |
36 | export { send };
37 |
--------------------------------------------------------------------------------
/src/utils/send-email.ts:
--------------------------------------------------------------------------------
1 | import { createTransport } from 'nodemailer';
2 | import { type Attachment } from 'nodemailer/lib/mailer';
3 | import { config } from '../config';
4 |
5 | const transporter = createTransport({
6 | service: config.mail.service,
7 | auth: {
8 | user: config.mail.username,
9 | pass: config.mail.password,
10 | },
11 | });
12 |
13 | async function sendEmail(
14 | subject: string,
15 | message: string,
16 | to: string,
17 | from: string,
18 | attachments: Attachment[] = [],
19 | ) {
20 | const options = {
21 | from,
22 | to,
23 | subject,
24 | html: message,
25 | attachments,
26 | };
27 |
28 | return new Promise((resolve, reject) => {
29 | transporter.sendMail(options, (err, info) => {
30 | if (err) {
31 | console.error('Email sending failed:', err);
32 | reject(new Error(err.message));
33 | } else {
34 | console.log('Email sent:', info.messageId);
35 | resolve(info.messageId);
36 | }
37 | });
38 | });
39 | }
40 |
41 | export { sendEmail };
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 1ilit
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.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "description": "Server that handles form submission for drawDB",
5 | "scripts": {
6 | "build": "tsc",
7 | "start": "node dist/index.js",
8 | "dev": "nodemon src/index.ts",
9 | "lint": "eslint . --ext .ts",
10 | "lint:fix": "eslint . --ext .ts --fix",
11 | "format": "prettier --write \"src/**/*.ts\"",
12 | "format:check": "prettier --check \"src/**/*.ts\"",
13 | "test": "echo \"Error: no test specified\" && exit 1"
14 | },
15 | "author": "1ilit",
16 | "license": "ISC",
17 | "devDependencies": {
18 | "@eslint/js": "^9.25.0",
19 | "@types/cors": "^2.8.17",
20 | "@types/dotenv": "^6.1.1",
21 | "@types/express": "^5.0.1",
22 | "@types/node": "^22.14.1",
23 | "@types/nodemailer": "^6.4.17",
24 | "eslint": "^9.25.0",
25 | "globals": "^16.0.0",
26 | "nodemon": "^3.1.9",
27 | "prettier": "^3.5.3",
28 | "typescript": "^5.8.3",
29 | "typescript-eslint": "^8.30.1"
30 | },
31 | "dependencies": {
32 | "axios": "^1.8.4",
33 | "cors": "^2.8.5",
34 | "dotenv": "^16.5.0",
35 | "express": "^4.21.2",
36 | "nodemailer": "^6.10.1",
37 | "ts-node": "^10.9.2"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/styles/email-styles.ts:
--------------------------------------------------------------------------------
1 | export const emailStyles =
2 | '';
3 |
--------------------------------------------------------------------------------
/src/controllers/gist-controller.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-unused-vars */
2 | import axios, { type AxiosError } from 'axios';
3 | import { Request, Response } from 'express';
4 | import { IGistCommitItem } from '../interfaces/gist-commit-item';
5 | import { IGistFile } from '../interfaces/gist-file';
6 | import { gistsBaseUrl, headers } from '../constants/gist-constants';
7 | import { GistService } from '../services/gist-service';
8 |
9 | async function get(req: Request, res: Response) {
10 | try {
11 | const { data } = await axios.get(`${gistsBaseUrl}/${req.params.id}`, {
12 | headers,
13 | });
14 |
15 | const {
16 | owner,
17 | history,
18 | forks,
19 | user,
20 | url,
21 | forks_url,
22 | commits_url,
23 | git_pull_url,
24 | git_push_url,
25 | html_url,
26 | comments_url,
27 | ...rest
28 | } = data;
29 |
30 | const cleanedFiles = Object.fromEntries(
31 | Object.entries(rest.files as Record).map(
32 | ([filename, { raw_url, ...fileWithoutRaw }]) => [filename, fileWithoutRaw],
33 | ),
34 | );
35 |
36 | res.status(200).json({
37 | success: true,
38 | data: { ...rest, files: cleanedFiles },
39 | });
40 | } catch (e) {
41 | console.error(e);
42 | if ((e as AxiosError).status === 404) {
43 | res.status(404).json({
44 | success: false,
45 | message: 'Gist not found',
46 | });
47 | } else {
48 | res.status(500).json({
49 | success: false,
50 | message: 'Something went wrong',
51 | });
52 | }
53 | }
54 | }
55 |
56 | async function create(req: Request, res: Response) {
57 | try {
58 | const { description, filename, content, public: isGistPublic } = req.body;
59 |
60 | const { data } = await axios.post(
61 | gistsBaseUrl,
62 | {
63 | description: description || '',
64 | public: isGistPublic || false,
65 | files: {
66 | [filename]: { content },
67 | },
68 | },
69 | { headers },
70 | );
71 |
72 | const returnData = {
73 | id: data.id,
74 | files: data.files,
75 | };
76 |
77 | res.status(200).json({
78 | success: true,
79 | data: returnData,
80 | });
81 | } catch (e) {
82 | console.error(e);
83 | res.status(500).json({
84 | success: false,
85 | message: 'Something went wrong',
86 | });
87 | }
88 | }
89 |
90 | async function update(req: Request, res: Response) {
91 | try {
92 | const { filename, content } = req.body;
93 |
94 | const { data } = await axios.patch(
95 | `${gistsBaseUrl}/${req.params.id}`,
96 | {
97 | files: {
98 | [filename]: { content },
99 | },
100 | },
101 | { headers },
102 | );
103 |
104 | let deleted = false;
105 | if (!Object.entries(data.files).length) {
106 | GistService.deleteGist(req.params.id);
107 | deleted = true;
108 | }
109 |
110 | res.status(200).json({
111 | deleted,
112 | success: true,
113 | message: 'Gist updated',
114 | });
115 | } catch (e) {
116 | console.error(e);
117 | if ((e as AxiosError).status === 404) {
118 | res.status(404).json({
119 | success: false,
120 | message: 'Gist not found',
121 | });
122 | } else {
123 | res.status(500).json({
124 | success: false,
125 | message: 'Something went wrong',
126 | });
127 | }
128 | }
129 | }
130 |
131 | async function del(req: Request, res: Response) {
132 | try {
133 | GistService.deleteGist(req.params.id);
134 |
135 | res.status(200).json({
136 | success: true,
137 | message: 'Gist deleted',
138 | });
139 | } catch (e) {
140 | console.error(e);
141 | if ((e as AxiosError).status === 404) {
142 | res.status(404).json({
143 | success: false,
144 | message: 'Gist not found',
145 | });
146 | } else {
147 | res.status(500).json({
148 | success: false,
149 | message: 'Something went wrong',
150 | });
151 | }
152 | }
153 | }
154 |
155 | async function getCommits(req: Request, res: Response) {
156 | try {
157 | const page = req.query.page ? parseInt(req.query.page as string) : undefined;
158 | const perPage = req.query.per_page ? parseInt(req.query.per_page as string) : undefined;
159 | const data = await GistService.getCommits(req.params.id, perPage, page);
160 |
161 | const cleanData = data.map((x: IGistCommitItem) => {
162 | const { user, url, ...rest } = x;
163 | return rest;
164 | });
165 |
166 | res.status(200).json({
167 | success: true,
168 | data: cleanData,
169 | });
170 | } catch (e) {
171 | if ((e as AxiosError).status === 404) {
172 | res.status(404).json({
173 | success: false,
174 | message: 'Gist not found',
175 | });
176 | } else {
177 | res.status(500).json({
178 | success: false,
179 | message: 'Something went wrong',
180 | });
181 | }
182 | }
183 | }
184 |
185 | async function getRevision(req: Request, res: Response) {
186 | try {
187 | const data = await GistService.getCommit(req.params.id, req.params.sha);
188 |
189 | const {
190 | owner,
191 | history,
192 | forks,
193 | user,
194 | url,
195 | forks_url,
196 | commits_url,
197 | git_pull_url,
198 | git_push_url,
199 | html_url,
200 | comments_url,
201 | ...rest
202 | } = data;
203 |
204 | const cleanedFiles = Object.fromEntries(
205 | Object.entries(rest.files as Record).map(
206 | ([filename, { raw_url, ...fileWithoutRaw }]) => [filename, fileWithoutRaw],
207 | ),
208 | );
209 |
210 | res.status(200).json({
211 | success: true,
212 | data: { ...rest, files: cleanedFiles },
213 | });
214 | } catch (e) {
215 | if ((e as AxiosError).status === 404) {
216 | res.status(404).json({
217 | success: false,
218 | message: 'Gist not found',
219 | });
220 | } else {
221 | res.status(500).json({
222 | success: false,
223 | message: 'Something went wrong',
224 | });
225 | }
226 | }
227 | }
228 |
229 | async function getRevisionsForFile(req: Request, res: Response) {
230 | try {
231 | const gistId = req.params.id;
232 | const file = req.params.file;
233 |
234 | const cursor = req.query.cursor as string;
235 | const limit = req.query.limit ? parseInt(req.query.limit as string) : 10;
236 |
237 | const batchSize = Math.max(limit * 2, 50);
238 | let page = 1;
239 | let startProcessing = !cursor;
240 |
241 | const versionsWithChanges: IGistCommitItem[] = [];
242 | let hasMore = true;
243 | let nextCursor: string | null = null;
244 |
245 | while (versionsWithChanges.length < limit && hasMore) {
246 | const commitsBatch = await GistService.getCommits(gistId, batchSize, page);
247 |
248 | if (commitsBatch.length === 0) {
249 | hasMore = false;
250 | break;
251 | }
252 |
253 | for (let i = 0; i < commitsBatch.length && versionsWithChanges.length < limit; i++) {
254 | const currentCommit = commitsBatch[i];
255 |
256 | if (!startProcessing) {
257 | if (currentCommit.version === cursor) {
258 | startProcessing = true;
259 | }
260 | continue;
261 | }
262 |
263 | if (versionsWithChanges.length === 0) {
264 | const version = await GistService.getCommit(gistId, currentCommit.version);
265 | if (version.files[file]) {
266 | versionsWithChanges.push(currentCommit);
267 | }
268 | continue;
269 | }
270 |
271 | const lastAddedCommit = versionsWithChanges[versionsWithChanges.length - 1];
272 |
273 | try {
274 | const [lastVersion, currentVersion] = await Promise.all([
275 | GistService.getCommit(gistId, lastAddedCommit.version),
276 | GistService.getCommit(gistId, currentCommit.version),
277 | ]);
278 |
279 | if (!currentVersion.files[file]) {
280 | break;
281 | }
282 |
283 | if (!lastVersion.files[file]) {
284 | versionsWithChanges.push(currentCommit);
285 | continue;
286 | }
287 |
288 | const lastContent = lastVersion.files[file].content;
289 | const currentContent = currentVersion.files[file].content;
290 |
291 | if (lastContent !== currentContent) {
292 | versionsWithChanges.push(currentCommit);
293 | }
294 | } catch (error) {
295 | console.error(`Error comparing versions:`, error);
296 | versionsWithChanges.push(currentCommit);
297 | }
298 | }
299 |
300 | if (commitsBatch.length < batchSize) {
301 | hasMore = false;
302 | } else {
303 | page++;
304 | }
305 | }
306 |
307 | if (versionsWithChanges.length === limit) {
308 | nextCursor = versionsWithChanges[versionsWithChanges.length - 1].version;
309 | }
310 |
311 | const versions = versionsWithChanges.map((v: IGistCommitItem) => {
312 | const { user, url, ...rest } = v;
313 | return rest;
314 | });
315 |
316 | res.status(200).json({
317 | success: true,
318 | data: versions,
319 | pagination: {
320 | cursor: nextCursor,
321 | hasMore: nextCursor !== null,
322 | limit,
323 | count: versions.length,
324 | },
325 | });
326 | } catch (e) {
327 | if ((e as AxiosError).status === 404) {
328 | res.status(404).json({
329 | success: false,
330 | message: 'Gist not found',
331 | });
332 | } else {
333 | res.status(500).json({
334 | success: false,
335 | message: 'Something went wrong',
336 | });
337 | }
338 | }
339 | }
340 |
341 | export { get, create, del, update, getCommits, getRevision, getRevisionsForFile };
342 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig to read more about this file */
4 |
5 | /* Projects */
6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
12 |
13 | /* Language and Environment */
14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
16 | // "jsx": "preserve", /* Specify what JSX code is generated. */
17 | // "libReplacement": true, /* Enable lib replacement. */
18 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
19 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
20 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
21 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
22 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
23 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
24 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
25 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
26 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
27 |
28 | /* Modules */
29 | "module": "commonjs", /* Specify what module code is generated. */
30 | // "rootDir": "./", /* Specify the root folder within your source files. */
31 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
32 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
33 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
34 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
35 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
36 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */
37 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
38 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
39 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
40 | // "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */
41 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
42 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
43 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
44 | // "noUncheckedSideEffectImports": true, /* Check side effect imports. */
45 | // "resolveJsonModule": true, /* Enable importing .json files. */
46 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
47 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
48 |
49 | /* JavaScript Support */
50 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
51 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
52 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
53 |
54 | /* Emit */
55 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
56 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
57 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
58 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
59 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
60 | // "noEmit": true, /* Disable emitting files from a compilation. */
61 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
62 | "outDir": "./dist", /* Specify an output folder for all emitted files. */
63 | // "removeComments": true, /* Disable emitting comments. */
64 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
65 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
66 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
67 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
68 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
69 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
70 | // "newLine": "crlf", /* Set the newline character for emitting files. */
71 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
72 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
73 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
74 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
75 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
76 |
77 | /* Interop Constraints */
78 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
79 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
80 | // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
81 | // "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */
82 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
83 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
84 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
85 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
86 |
87 | /* Type Checking */
88 | "strict": true, /* Enable all strict type-checking options. */
89 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
90 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
91 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
92 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
93 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
94 | // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
95 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
96 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
97 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
98 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
99 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
100 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
101 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
102 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
103 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
104 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
105 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
106 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
107 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
108 |
109 | /* Completeness */
110 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
111 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
112 | },
113 | "include": ["src/**/*"],
114 | "exclude": ["node_modules"]
115 | }
116 |
--------------------------------------------------------------------------------