breakpoint-utils 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,186 @@
1
+ # breakpoint-utils
2
+
3
+ Lightweight utilities for querying responsive breakpoints and viewport width in JavaScript and React.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install breakpoint-utils
9
+ ```
10
+
11
+ ## Features
12
+
13
+ - **Configurable breakpoints** - Use defaults or define your own
14
+ - **Responsive utilities** - `up`, `down`, `only`, `not`, `between`
15
+ - **React hook** - `useViewport` for reactive viewport width
16
+ - **Lightweight** - Minimal dependencies
17
+ - **TypeScript** - Full type safety
18
+
19
+ ## Quick Start
20
+
21
+ ### Default Breakpoints
22
+
23
+ ```typescript
24
+ import { up, down, only } from 'breakpoint-utils';
25
+
26
+ // Default breakpoints: xs: 480, sm: 640, md: 768, lg: 1024, xl: 1280, 2xl: 1536
27
+
28
+ if (up('md')) {
29
+ // viewport >= 768px
30
+ }
31
+
32
+ if (down('lg')) {
33
+ // viewport < 1024px
34
+ }
35
+
36
+ if (only('sm')) {
37
+ // 640px <= viewport < 768px
38
+ }
39
+ ```
40
+
41
+ ### Custom Breakpoints
42
+
43
+ ```typescript
44
+ import { configure, up, down } from 'breakpoint-utils';
45
+
46
+ // Define your own breakpoints
47
+ configure({
48
+ mobile: 320,
49
+ tablet: 768,
50
+ desktop: 1024,
51
+ wide: 1440,
52
+ });
53
+
54
+ if (up('tablet')) {
55
+ // viewport >= 768px
56
+ }
57
+ ```
58
+
59
+ ## API
60
+
61
+ ### `configure(breakpoints)`
62
+
63
+ Set custom breakpoints. Must be called before using utility functions.
64
+
65
+ ```typescript
66
+ configure({
67
+ small: 600,
68
+ medium: 900,
69
+ large: 1200,
70
+ });
71
+ ```
72
+
73
+ ### `getBreakpoints()`
74
+
75
+ Get the current breakpoint configuration.
76
+
77
+ ```typescript
78
+ const breakpoints = getBreakpoints();
79
+ // { xs: 480, sm: 640, ... } or your custom config
80
+ ```
81
+
82
+ ### `up(size)`
83
+
84
+ Returns `true` if viewport width is >= the breakpoint.
85
+
86
+ ```typescript
87
+ up('md') // viewport >= 768px
88
+ ```
89
+
90
+ ### `down(size)`
91
+
92
+ Returns `true` if viewport width is < the breakpoint.
93
+
94
+ ```typescript
95
+ down('lg') // viewport < 1024px
96
+ ```
97
+
98
+ ### `only(size)`
99
+
100
+ Returns `true` if viewport is within the breakpoint range (>= size and < next breakpoint).
101
+
102
+ ```typescript
103
+ only('md') // 768px <= viewport < 1024px
104
+ ```
105
+
106
+ ### `not(size)`
107
+
108
+ Returns `true` if viewport is outside the breakpoint range.
109
+
110
+ ```typescript
111
+ not('md') // viewport < 768px OR viewport >= 1024px
112
+ ```
113
+
114
+ ### `between(minSize, maxSize)`
115
+
116
+ Returns `true` if viewport is between two breakpoints.
117
+
118
+ ```typescript
119
+ between('sm', 'lg') // 640px <= viewport < 1024px
120
+ ```
121
+
122
+ ## React Hook
123
+
124
+ ### `useViewport()`
125
+
126
+ React hook that returns the current viewport width and updates on resize.
127
+
128
+ ```typescript
129
+ import { useViewport, configure, up } from 'breakpoint-utils';
130
+
131
+ // Optional: configure custom breakpoints
132
+ configure({
133
+ mobile: 375,
134
+ tablet: 768,
135
+ desktop: 1024,
136
+ });
137
+
138
+ function MyComponent() {
139
+ const width = useViewport();
140
+
141
+ return (
142
+ <div>
143
+ <p>Viewport width: {width}px</p>
144
+ {up('tablet') && <p>Tablet or larger</p>}
145
+ </div>
146
+ );
147
+ }
148
+ ```
149
+
150
+ ## Direct Viewport Access
151
+
152
+ ```typescript
153
+ import { viewport } from 'breakpoint-utils';
154
+
155
+ const width = viewport.getWidth();
156
+
157
+ const unsubscribe = viewport.onResize(() => {
158
+ console.log('Viewport resized:', viewport.getWidth());
159
+ });
160
+
161
+ // Clean up
162
+ unsubscribe();
163
+ ```
164
+
165
+ ## TypeScript
166
+
167
+ ```typescript
168
+ import { configure, up, type Breakpoints } from 'breakpoint-utils';
169
+
170
+ const myBreakpoints = {
171
+ xs: 320,
172
+ sm: 640,
173
+ md: 768,
174
+ lg: 1024,
175
+ } satisfies Breakpoints;
176
+
177
+ configure(myBreakpoints);
178
+
179
+ // Type-safe breakpoint names
180
+ type MyBreakpoint = keyof typeof myBreakpoints;
181
+ const size: MyBreakpoint = 'md';
182
+ ```
183
+
184
+ ## License
185
+
186
+ MIT
@@ -0,0 +1,9 @@
1
+ export type Breakpoints = Record<string, number>;
2
+ export declare const configure: <T extends Breakpoints>(customBreakpoints: T) => void;
3
+ export declare const getBreakpoints: () => Breakpoints;
4
+ export declare const up: (size: string) => boolean;
5
+ export declare const down: (size: string) => boolean;
6
+ export declare const only: (size: string) => boolean;
7
+ export declare const not: (size: string) => boolean;
8
+ export declare const between: (minSize: string, maxSize: string) => boolean;
9
+ //# sourceMappingURL=breakpoints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breakpoints.d.ts","sourceRoot":"","sources":["../src/breakpoints.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAWjD,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,WAAW,EAAE,mBAAmB,CAAC,SAGpE,CAAC;AAEF,eAAO,MAAM,cAAc,mBAA2B,CAAC;AAcvD,eAAO,MAAM,EAAE,GAAI,MAAM,MAAM,YAG9B,CAAC;AAEF,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,YAGhC,CAAC;AAEF,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,YAMhC,CAAC;AAEF,eAAO,MAAM,GAAG,GAAI,MAAM,MAAM,YAM/B,CAAC;AAEF,eAAO,MAAM,OAAO,GAAI,SAAS,MAAM,EAAE,SAAS,MAAM,YAOvD,CAAC"}
@@ -0,0 +1,61 @@
1
+ import viewport from './viewport';
2
+ const defaultBreakpoints = {
3
+ xs: 480,
4
+ sm: 640,
5
+ md: 768,
6
+ lg: 1024,
7
+ xl: 1280,
8
+ '2xl': 1536,
9
+ };
10
+ let currentBreakpoints = { ...defaultBreakpoints };
11
+ let sortedBreakpoints = [];
12
+ const updateSortedBreakpoints = () => {
13
+ sortedBreakpoints = Object.entries(currentBreakpoints).sort((a, b) => a[1] - b[1]);
14
+ };
15
+ updateSortedBreakpoints();
16
+ export const configure = (customBreakpoints) => {
17
+ currentBreakpoints = customBreakpoints;
18
+ updateSortedBreakpoints();
19
+ };
20
+ export const getBreakpoints = () => currentBreakpoints;
21
+ const validateBreakpoint = (size) => {
22
+ if (!(size in currentBreakpoints)) {
23
+ throw new Error(`Invalid breakpoint: "${size}". Available breakpoints: ${Object.keys(currentBreakpoints).join(', ')}`);
24
+ }
25
+ };
26
+ const getNextBreakpoint = (size) => {
27
+ const currentIndex = sortedBreakpoints.findIndex(([name]) => name === size);
28
+ const next = sortedBreakpoints[currentIndex + 1];
29
+ return next ? next[1] : Infinity;
30
+ };
31
+ export const up = (size) => {
32
+ validateBreakpoint(size);
33
+ return viewport.getWidth() >= currentBreakpoints[size];
34
+ };
35
+ export const down = (size) => {
36
+ validateBreakpoint(size);
37
+ return viewport.getWidth() < currentBreakpoints[size];
38
+ };
39
+ export const only = (size) => {
40
+ validateBreakpoint(size);
41
+ const width = viewport.getWidth();
42
+ const min = currentBreakpoints[size];
43
+ const max = getNextBreakpoint(size);
44
+ return width >= min && width < max;
45
+ };
46
+ export const not = (size) => {
47
+ validateBreakpoint(size);
48
+ const width = viewport.getWidth();
49
+ const min = currentBreakpoints[size];
50
+ const max = getNextBreakpoint(size);
51
+ return width < min || width >= max;
52
+ };
53
+ export const between = (minSize, maxSize) => {
54
+ validateBreakpoint(minSize);
55
+ validateBreakpoint(maxSize);
56
+ const width = viewport.getWidth();
57
+ const min = currentBreakpoints[minSize];
58
+ const max = currentBreakpoints[maxSize];
59
+ return width >= min && width < max;
60
+ };
61
+ //# sourceMappingURL=breakpoints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breakpoints.js","sourceRoot":"","sources":["../src/breakpoints.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { configure, getBreakpoints, up, down, only, not, between } from './breakpoints';
2
+ export type { Breakpoints } from './breakpoints';
3
+ export { default as useViewport } from './useViewport';
4
+ export { default as viewport } from './viewport';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxF,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { configure, getBreakpoints, up, down, only, not, between } from './breakpoints';
2
+ export { default as useViewport } from './useViewport';
3
+ export { default as viewport } from './viewport';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExF,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ declare const useViewport: () => number;
2
+ export default useViewport;
3
+ //# sourceMappingURL=useViewport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useViewport.d.ts","sourceRoot":"","sources":["../src/useViewport.ts"],"names":[],"mappings":"AAGA,QAAA,MAAM,WAAW,cAahB,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { useEffect, useState } from 'react';
2
+ import viewport from './viewport';
3
+ const useViewport = () => {
4
+ const [width, setWidth] = useState(viewport.getWidth());
5
+ useEffect(() => {
6
+ const handler = () => setWidth(viewport.getWidth());
7
+ const removeHandler = viewport.onResize(handler);
8
+ return () => {
9
+ removeHandler();
10
+ };
11
+ }, []);
12
+ return width;
13
+ };
14
+ export default useViewport;
15
+ //# sourceMappingURL=useViewport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useViewport.js","sourceRoot":"","sources":["../src/useViewport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,QAAQ,MAAM,YAAY,CAAC;AAElC,MAAM,WAAW,GAAG,GAAG,EAAE;IACvB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;IAExD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QACpD,MAAM,aAAa,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAEjD,OAAO,GAAG,EAAE;YACV,aAAa,EAAE,CAAC;QAClB,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF,eAAe,WAAW,CAAC"}
@@ -0,0 +1,11 @@
1
+ declare class ViewportHelper {
2
+ private width;
3
+ private eventHandlers;
4
+ constructor();
5
+ private handleResize;
6
+ getWidth(): number;
7
+ onResize(handler: () => void): () => void;
8
+ }
9
+ declare const viewportHelper: ViewportHelper;
10
+ export default viewportHelper;
11
+ //# sourceMappingURL=viewport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewport.d.ts","sourceRoot":"","sources":["../src/viewport.ts"],"names":[],"mappings":"AAAA,cAAM,cAAc;IAClB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,aAAa,CAAiB;;IAUtC,OAAO,CAAC,YAAY,CAGlB;IAEF,QAAQ;IAIR,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI;CAM7B;AAED,QAAA,MAAM,cAAc,gBAAuB,CAAC;AAC5C,eAAe,cAAc,CAAC"}
@@ -0,0 +1,25 @@
1
+ class ViewportHelper {
2
+ constructor() {
3
+ this.handleResize = () => {
4
+ this.width = window.innerWidth;
5
+ this.eventHandlers.forEach(handler => handler());
6
+ };
7
+ this.width = typeof window !== 'undefined' ? window.innerWidth : 0;
8
+ this.eventHandlers = [];
9
+ if (typeof window !== 'undefined') {
10
+ window.addEventListener('resize', this.handleResize);
11
+ }
12
+ }
13
+ getWidth() {
14
+ return this.width;
15
+ }
16
+ onResize(handler) {
17
+ this.eventHandlers.push(handler);
18
+ return () => {
19
+ this.eventHandlers = this.eventHandlers.filter(h => h !== handler);
20
+ };
21
+ }
22
+ }
23
+ const viewportHelper = new ViewportHelper();
24
+ export default viewportHelper;
25
+ //# sourceMappingURL=viewport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewport.js","sourceRoot":"","sources":["../src/viewport.ts"],"names":[],"mappings":"AAAA,MAAM,cAAc;IAIlB;QAQQ,iBAAY,GAAG,GAAG,EAAE;YAC1B,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC;YAC/B,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QACnD,CAAC,CAAC;QAVA,IAAI,CAAC,KAAK,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QACnE,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QACxB,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAOD,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,QAAQ,CAAC,OAAmB;QAC1B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACjC,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC;QACrE,CAAC,CAAC;IACJ,CAAC;CACF;AAED,MAAM,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;AAC5C,eAAe,cAAc,CAAC"}
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "breakpoint-utils",
3
+ "version": "1.0.0",
4
+ "description": "Viewport width utility with breakpoint functions for React",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "dev": "tsc --watch"
10
+ },
11
+ "peerDependencies": {
12
+ "react": ">=16.8.0"
13
+ },
14
+ "devDependencies": {
15
+ "@types/react": "^18.2.0",
16
+ "react": "^18.2.0",
17
+ "typescript": "^5.0.0"
18
+ },
19
+ "keywords": [
20
+ "breakpoints",
21
+ "viewport",
22
+ "responsive",
23
+ "react"
24
+ ],
25
+ "license": "MIT"
26
+ }
@@ -0,0 +1,75 @@
1
+ import viewport from './viewport';
2
+
3
+ const defaultBreakpoints = {
4
+ xs: 480,
5
+ sm: 640,
6
+ md: 768,
7
+ lg: 1024,
8
+ xl: 1280,
9
+ '2xl': 1536,
10
+ } as const;
11
+
12
+ export type Breakpoints = Record<string, number>;
13
+
14
+ let currentBreakpoints: Breakpoints = { ...defaultBreakpoints };
15
+ let sortedBreakpoints: [string, number][] = [];
16
+
17
+ const updateSortedBreakpoints = () => {
18
+ sortedBreakpoints = Object.entries(currentBreakpoints).sort((a, b) => a[1] - b[1]);
19
+ };
20
+
21
+ updateSortedBreakpoints();
22
+
23
+ export const configure = <T extends Breakpoints>(customBreakpoints: T) => {
24
+ currentBreakpoints = customBreakpoints;
25
+ updateSortedBreakpoints();
26
+ };
27
+
28
+ export const getBreakpoints = () => currentBreakpoints;
29
+
30
+ const validateBreakpoint = (size: string): void => {
31
+ if (!(size in currentBreakpoints)) {
32
+ throw new Error(`Invalid breakpoint: "${size}". Available breakpoints: ${Object.keys(currentBreakpoints).join(', ')}`);
33
+ }
34
+ };
35
+
36
+ const getNextBreakpoint = (size: string): number => {
37
+ const currentIndex = sortedBreakpoints.findIndex(([name]) => name === size);
38
+ const next = sortedBreakpoints[currentIndex + 1];
39
+ return next ? next[1] : Infinity;
40
+ };
41
+
42
+ export const up = (size: string) => {
43
+ validateBreakpoint(size);
44
+ return viewport.getWidth() >= currentBreakpoints[size];
45
+ };
46
+
47
+ export const down = (size: string) => {
48
+ validateBreakpoint(size);
49
+ return viewport.getWidth() < currentBreakpoints[size];
50
+ };
51
+
52
+ export const only = (size: string) => {
53
+ validateBreakpoint(size);
54
+ const width = viewport.getWidth();
55
+ const min = currentBreakpoints[size];
56
+ const max = getNextBreakpoint(size);
57
+ return width >= min && width < max;
58
+ };
59
+
60
+ export const not = (size: string) => {
61
+ validateBreakpoint(size);
62
+ const width = viewport.getWidth();
63
+ const min = currentBreakpoints[size];
64
+ const max = getNextBreakpoint(size);
65
+ return width < min || width >= max;
66
+ };
67
+
68
+ export const between = (minSize: string, maxSize: string) => {
69
+ validateBreakpoint(minSize);
70
+ validateBreakpoint(maxSize);
71
+ const width = viewport.getWidth();
72
+ const min = currentBreakpoints[minSize];
73
+ const max = currentBreakpoints[maxSize];
74
+ return width >= min && width < max;
75
+ };
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { configure, getBreakpoints, up, down, only, not, between } from './breakpoints';
2
+ export type { Breakpoints } from './breakpoints';
3
+ export { default as useViewport } from './useViewport';
4
+ export { default as viewport } from './viewport';
@@ -0,0 +1,19 @@
1
+ import { useEffect, useState } from 'react';
2
+ import viewport from './viewport';
3
+
4
+ const useViewport = () => {
5
+ const [width, setWidth] = useState(viewport.getWidth());
6
+
7
+ useEffect(() => {
8
+ const handler = () => setWidth(viewport.getWidth());
9
+ const removeHandler = viewport.onResize(handler);
10
+
11
+ return () => {
12
+ removeHandler();
13
+ };
14
+ }, []);
15
+
16
+ return width;
17
+ };
18
+
19
+ export default useViewport;
@@ -0,0 +1,31 @@
1
+ class ViewportHelper {
2
+ private width: number;
3
+ private eventHandlers: (() => void)[];
4
+
5
+ constructor() {
6
+ this.width = typeof window !== 'undefined' ? window.innerWidth : 0;
7
+ this.eventHandlers = [];
8
+ if (typeof window !== 'undefined') {
9
+ window.addEventListener('resize', this.handleResize);
10
+ }
11
+ }
12
+
13
+ private handleResize = () => {
14
+ this.width = window.innerWidth;
15
+ this.eventHandlers.forEach(handler => handler());
16
+ };
17
+
18
+ getWidth() {
19
+ return this.width;
20
+ }
21
+
22
+ onResize(handler: () => void) {
23
+ this.eventHandlers.push(handler);
24
+ return () => {
25
+ this.eventHandlers = this.eventHandlers.filter(h => h !== handler);
26
+ };
27
+ }
28
+ }
29
+
30
+ const viewportHelper = new ViewportHelper();
31
+ export default viewportHelper;
package/tsconfig.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2018",
4
+ "module": "ESNext",
5
+ "moduleResolution": "node",
6
+ "lib": ["DOM", "ES2018"],
7
+ "declaration": true,
8
+ "declarationMap": true,
9
+ "sourceMap": true,
10
+ "outDir": "./dist",
11
+ "rootDir": "./src",
12
+ "strict": true,
13
+ "esModuleInterop": true,
14
+ "skipLibCheck": true,
15
+ "forceConsistentCasingInFileNames": true
16
+ },
17
+ "include": ["src/**/*"],
18
+ "exclude": ["node_modules", "dist"]
19
+ }