glotstack 0.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/.prettierrc ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "tabWidth": 2,
3
+ "useTabs": false,
4
+ "semi": false,
5
+ "singleQuote": true,
6
+ "jsxSingleQuote": true,
7
+ "jsxBracketSameLine": false,
8
+ "quoteProps": "as-needed",
9
+ "printWidth": 80
10
+ }
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+ export type LocaleRegion = 'en' | 'en-US' | 'en-US-genz' | 'fr-FR' | 'de-DE' | 'nl-NL' | 'jp-JP' | string;
3
+ export interface TranslationLeaf {
4
+ value: string;
5
+ context?: string;
6
+ }
7
+ export interface Translations {
8
+ [key: string]: Translations | TranslationLeaf;
9
+ }
10
+ export interface ContextType {
11
+ translations: Translations;
12
+ requireTranslations: (localeRegion: string) => Promise<unknown>;
13
+ setLocale: (locale: LocaleRegion) => Promise<unknown>;
14
+ locale: string | null;
15
+ t: (key: string, options?: {
16
+ locale?: LocaleRegion;
17
+ }) => string;
18
+ }
19
+ export declare const GlotstackContext: React.Context<ContextType>;
20
+ export declare const Provider: ({ children, locale: defaultLocale, onUpdated, initialTranslations, onLocaleChange }: {
21
+ children: React.ReactNode;
22
+ locale: string;
23
+ onUpdated: (value: ContextType) => void;
24
+ initialTranslations?: Translations;
25
+ onLocaleChange: (locale: string | null, value: ContextType) => void;
26
+ }) => import("react/jsx-runtime").JSX.Element;
27
+ export declare const useTranslations: (_options?: Record<never, never>) => ContextType;
package/dist/index.js ADDED
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ exports.useTranslations = exports.Provider = exports.GlotstackContext = void 0;
36
+ const jsx_runtime_1 = require("react/jsx-runtime");
37
+ const React = __importStar(require("react"));
38
+ const object_1 = require("./util/object");
39
+ exports.GlotstackContext = React.createContext({
40
+ translations: {},
41
+ requireTranslations: () => Promise.resolve(),
42
+ setLocale: (_locale) => Promise.resolve(),
43
+ locale: null,
44
+ t: () => '',
45
+ });
46
+ const Provider = ({ children, locale: defaultLocale, onUpdated, initialTranslations, onLocaleChange }) => {
47
+ const [translations, setTranslations] = React.useState(initialTranslations !== null && initialTranslations !== void 0 ? initialTranslations : {});
48
+ const loadingRef = React.useRef({});
49
+ const [locale, setLocalLocale] = React.useState(defaultLocale);
50
+ React.useEffect(() => {
51
+ setLocalLocale(defaultLocale);
52
+ }, [defaultLocale]);
53
+ const requireTranslations = React.useMemo(() => {
54
+ return (locale) => {
55
+ var _a;
56
+ const current = translations === null || translations === void 0 ? void 0 : translations[locale];
57
+ if (current != null) {
58
+ return Promise.resolve();
59
+ }
60
+ if (((_a = loadingRef.current) === null || _a === void 0 ? void 0 : _a[locale]) == null) {
61
+ loadingRef.current[locale] = Promise.resolve(`${`domains/application/translations/${locale}.json`}`).then(s => __importStar(require(s)));
62
+ }
63
+ loadingRef.current[locale].then((Module) => __awaiter(void 0, void 0, void 0, function* () {
64
+ if ((Module === null || Module === void 0 ? void 0 : Module.default) != null) {
65
+ const data = yield (yield fetch(Module.default)).json();
66
+ setTranslations((0, object_1.merge)({}, translations, { [locale]: data }));
67
+ }
68
+ }));
69
+ return loadingRef.current[locale];
70
+ };
71
+ }, [locale, translations]);
72
+ const t = React.useMemo(() => (key, options) => {
73
+ var _a;
74
+ React.useEffect(() => {
75
+ var _a;
76
+ if ((options === null || options === void 0 ? void 0 : options.locale) == null) {
77
+ return;
78
+ }
79
+ (_a = requireTranslations(options === null || options === void 0 ? void 0 : options.locale)) === null || _a === void 0 ? void 0 : _a.then(() => {
80
+ if ((options === null || options === void 0 ? void 0 : options.locale) == null) {
81
+ return;
82
+ }
83
+ setLocale(options === null || options === void 0 ? void 0 : options.locale);
84
+ });
85
+ }, [locale]);
86
+ if (translations === null) {
87
+ return key;
88
+ }
89
+ const access = [...key.split('.')];
90
+ const localeTranslations = translations === null || translations === void 0 ? void 0 : translations[locale];
91
+ if (localeTranslations == null) {
92
+ return key;
93
+ }
94
+ const value = access.reduce((acc, key) => {
95
+ // @ts-expect-error expected
96
+ return acc === null || acc === void 0 ? void 0 : acc[key];
97
+ }, localeTranslations);
98
+ return ((_a = value === null || value === void 0 ? void 0 : value.value) !== null && _a !== void 0 ? _a : key);
99
+ }, [translations, requireTranslations]);
100
+ const setLocale = React.useMemo(() => (locale) => {
101
+ setLocalLocale(locale);
102
+ return requireTranslations(locale).then(_ => {
103
+ onLocaleChange(valueRef.current.locale, valueRef.current);
104
+ });
105
+ }, [setLocalLocale, requireTranslations]);
106
+ const valueRef = React.useRef({
107
+ requireTranslations,
108
+ locale,
109
+ translations: initialTranslations !== null && initialTranslations !== void 0 ? initialTranslations : {},
110
+ setLocale,
111
+ t
112
+ });
113
+ const current = valueRef.current;
114
+ React.useEffect(() => {
115
+ valueRef.current = {
116
+ translations,
117
+ requireTranslations,
118
+ setLocale,
119
+ locale,
120
+ t
121
+ };
122
+ if (!(0, object_1.isEqual)(valueRef.current, current)) {
123
+ onUpdated === null || onUpdated === void 0 ? void 0 : onUpdated(valueRef.current);
124
+ }
125
+ }, [locale, translations, requireTranslations]);
126
+ React.useEffect(() => {
127
+ requireTranslations(locale);
128
+ }, [requireTranslations, locale]);
129
+ return (0, jsx_runtime_1.jsx)(exports.GlotstackContext.Provider, { value: valueRef.current, children: children });
130
+ };
131
+ exports.Provider = Provider;
132
+ const useTranslations = (_options) => {
133
+ const context = React.useContext(exports.GlotstackContext);
134
+ return context;
135
+ };
136
+ exports.useTranslations = useTranslations;
137
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare function isObject(val: any): val is Record<string, any>;
2
+ type MergeTarget = Record<string | number | symbol, any>;
3
+ export declare function merge(target: MergeTarget, ...sources: MergeTarget[]): MergeTarget;
4
+ export declare function isEqual(a: any, b: any): boolean;
5
+ export {};
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isEqual = exports.merge = exports.isObject = void 0;
4
+ function isObject(val) {
5
+ return typeof val === 'object' && val !== null && !Array.isArray(val);
6
+ }
7
+ exports.isObject = isObject;
8
+ function _merge(target, source) {
9
+ if (!isObject(target) || !isObject(source))
10
+ return target;
11
+ for (const key of Object.keys(source)) {
12
+ const srcVal = source[key];
13
+ const tgtVal = target[key];
14
+ if (Array.isArray(srcVal) && Array.isArray(tgtVal)) {
15
+ target[key] = tgtVal.concat(srcVal);
16
+ }
17
+ else if (isObject(srcVal) && isObject(tgtVal)) {
18
+ _merge(tgtVal, srcVal);
19
+ }
20
+ else {
21
+ target[key] = srcVal;
22
+ }
23
+ }
24
+ return target;
25
+ }
26
+ function merge(target, ...sources) {
27
+ return sources.reduce((previous, current) => {
28
+ return merge(previous, current);
29
+ }, target);
30
+ }
31
+ exports.merge = merge;
32
+ function isEqual(a, b) {
33
+ if (a === b)
34
+ return true;
35
+ if (typeof a !== typeof b || a == null || b == null)
36
+ return false;
37
+ if (Array.isArray(a)) {
38
+ if (!Array.isArray(b) || a.length !== b.length)
39
+ return false;
40
+ return a.every((val, i) => isEqual(val, b[i]));
41
+ }
42
+ if (typeof a === 'object') {
43
+ const aKeys = Object.keys(a);
44
+ const bKeys = Object.keys(b);
45
+ if (aKeys.length !== bKeys.length)
46
+ return false;
47
+ return aKeys.every(key => b.hasOwnProperty(key) && isEqual(a[key], b[key]));
48
+ }
49
+ return false;
50
+ }
51
+ exports.isEqual = isEqual;
52
+ //# sourceMappingURL=object.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object.js","sourceRoot":"","sources":["../../src/util/object.ts"],"names":[],"mappings":";;;AAAA,SAAgB,QAAQ,CAAC,GAAQ;IAC/B,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;AACxE,CAAC;AAFD,4BAEC;AAED,SAAS,MAAM,CAAC,MAAW,EAAE,MAAW;IACtC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAE1D,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAE3B,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACnD,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACtC,CAAC;aAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAChD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;QACvB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAID,SAAgB,KAAK,CAAC,MAAmB,EAAE,GAAG,OAAsB;IAClE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE;QAC1C,OAAO,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC,EAAE,MAAM,CAAC,CAAA;AACZ,CAAC;AAJD,sBAIC;AAED,SAAgB,OAAO,CAAC,CAAM,EAAE,CAAM;IACpC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzB,IAAI,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,KAAK,CAAC;IAElE,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACrB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC7D,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAEhD,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAnBD,0BAmBC"}
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "glotstack",
3
+ "version": "0.0.0",
4
+ "main": "dist/index.js",
5
+ "author": "JD Cumpson",
6
+ "license": "MIT",
7
+ "private": false,
8
+ "dependencies": {
9
+ },
10
+ "devDependencies": {
11
+ "@types/react": "^18.3.1",
12
+ "react": "^18.3.1",
13
+ "typescript": "5.4.4",
14
+ "typescript-eslint": "^8.17.0"
15
+ },
16
+ "peerDependencies": {
17
+ "react": "^18.3.1"
18
+ },
19
+ "bin": {
20
+ "glotstack": "dist/glotstack-ci.js"
21
+ },
22
+ "scripts": {
23
+ "build": "tsc",
24
+ "prepublishOnly": "yarn run build"
25
+ }
26
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,150 @@
1
+ import * as React from 'react'
2
+ import { merge, isEqual } from './util/object'
3
+
4
+ export type LocaleRegion =
5
+ | 'en'
6
+ | 'en-US'
7
+ | 'en-US-genz'
8
+ | 'fr-FR'
9
+ | 'de-DE'
10
+ | 'nl-NL'
11
+ | 'jp-JP'
12
+ | string
13
+
14
+
15
+ export interface TranslationLeaf {
16
+ value: string
17
+ context?: string
18
+ }
19
+
20
+ export interface Translations {
21
+ [key: string]: Translations | TranslationLeaf
22
+ }
23
+
24
+ export interface ContextType {
25
+ translations: Translations
26
+ requireTranslations: (localeRegion: string) => Promise<unknown>
27
+ setLocale: (locale: LocaleRegion) => Promise<unknown>
28
+ locale: string | null
29
+ t: (key: string, options?: { locale?: LocaleRegion }) => string
30
+ }
31
+
32
+ export const GlotstackContext = React.createContext<ContextType>({
33
+ translations: {},
34
+ requireTranslations: () => Promise.resolve(),
35
+ setLocale: (_locale: LocaleRegion) => Promise.resolve(),
36
+ locale: null,
37
+ t: () => '',
38
+ })
39
+
40
+ export const Provider = ({ children, locale: defaultLocale, onUpdated, initialTranslations, onLocaleChange }: {
41
+ children: React.ReactNode; locale: string; onUpdated: (value: ContextType) => void
42
+ initialTranslations?: Translations
43
+ onLocaleChange: (locale: string | null, value: ContextType) => void
44
+ }) => {
45
+ const [translations, setTranslations] = React.useState<Translations>(initialTranslations ?? {})
46
+ const loadingRef = React.useRef<Record<string, Promise<{ default: string }>>>({})
47
+ const [locale, setLocalLocale] = React.useState(defaultLocale)
48
+
49
+ React.useEffect(() => {
50
+ setLocalLocale(defaultLocale)
51
+ }, [defaultLocale])
52
+
53
+ const requireTranslations = React.useMemo(() => {
54
+ return (locale: string) => {
55
+ const current = translations?.[locale]
56
+ if (current != null) {
57
+ return Promise.resolve()
58
+ }
59
+ if (loadingRef.current?.[locale] == null) {
60
+ loadingRef.current[locale] = import(`domains/application/translations/${locale}.json`)
61
+ }
62
+ loadingRef.current[locale].then(async (Module) => {
63
+ if (Module?.default != null) {
64
+ const data = await (await fetch(Module.default)).json()
65
+ setTranslations(merge({}, translations, { [locale]: data }))
66
+ }
67
+ })
68
+ return loadingRef.current[locale]
69
+ }
70
+ }, [locale, translations])
71
+
72
+ const t = React.useMemo(() => (key: string, options?: { locale?: LocaleRegion }) => {
73
+ React.useEffect(() => {
74
+ if (options?.locale == null) {
75
+ return
76
+ }
77
+ requireTranslations(options?.locale)?.then(() => {
78
+ if (options?.locale == null) {
79
+ return
80
+ }
81
+ setLocale(options?.locale)
82
+ })
83
+ }, [locale])
84
+
85
+ if (translations === null) {
86
+ return key
87
+ }
88
+
89
+ const access = [...key.split('.')] as [LocaleRegion, ...string[]]
90
+ const localeTranslations = translations?.[locale]
91
+
92
+ if (localeTranslations == null) {
93
+ return key
94
+ }
95
+
96
+ const value = access.reduce((acc: Translations[string], key) => {
97
+ // @ts-expect-error expected
98
+ return acc?.[key]
99
+ }, localeTranslations)
100
+
101
+ return (value?.value ?? key) as string
102
+ }, [translations, requireTranslations])
103
+
104
+ const setLocale = React.useMemo(() => (locale: LocaleRegion) => {
105
+ setLocalLocale(locale)
106
+ return requireTranslations(locale).then(_ => {
107
+ onLocaleChange(valueRef.current.locale, valueRef.current)
108
+ })
109
+ }, [setLocalLocale, requireTranslations])
110
+
111
+ const valueRef = React.useRef<ContextType>({
112
+ requireTranslations,
113
+ locale,
114
+ translations: initialTranslations ?? {},
115
+ setLocale,
116
+ t
117
+ })
118
+
119
+ const current = valueRef.current
120
+
121
+ React.useEffect(() => {
122
+ valueRef.current = {
123
+ translations,
124
+ requireTranslations,
125
+ setLocale,
126
+ locale,
127
+ t
128
+ }
129
+
130
+ if (!isEqual(valueRef.current, current)) {
131
+ onUpdated?.(valueRef.current)
132
+ }
133
+
134
+ }, [locale, translations, requireTranslations])
135
+
136
+ React.useEffect(() => {
137
+ requireTranslations(locale)
138
+ }, [requireTranslations, locale])
139
+
140
+ return <GlotstackContext.Provider value={valueRef.current}>
141
+ {children}
142
+ </GlotstackContext.Provider>
143
+ }
144
+
145
+
146
+ export const useTranslations = (_options?: Record<never, never>) => {
147
+ const context = React.useContext(GlotstackContext)
148
+ return context
149
+ }
150
+
@@ -0,0 +1,52 @@
1
+ export function isObject(val: any): val is Record<string, any> {
2
+ return typeof val === 'object' && val !== null && !Array.isArray(val);
3
+ }
4
+
5
+ function _merge(target: any, source: any): any {
6
+ if (!isObject(target) || !isObject(source)) return target;
7
+
8
+ for (const key of Object.keys(source)) {
9
+ const srcVal = source[key];
10
+ const tgtVal = target[key];
11
+
12
+ if (Array.isArray(srcVal) && Array.isArray(tgtVal)) {
13
+ target[key] = tgtVal.concat(srcVal);
14
+ } else if (isObject(srcVal) && isObject(tgtVal)) {
15
+ _merge(tgtVal, srcVal);
16
+ } else {
17
+ target[key] = srcVal;
18
+ }
19
+ }
20
+
21
+ return target;
22
+ }
23
+
24
+ type MergeTarget = Record<string|number|symbol, any>
25
+
26
+ export function merge(target: MergeTarget, ...sources: MergeTarget[]): MergeTarget {
27
+ return sources.reduce((previous, current) => {
28
+ return merge(previous, current);
29
+ }, target)
30
+ }
31
+
32
+ export function isEqual(a: any, b: any): boolean {
33
+ if (a === b) return true;
34
+
35
+ if (typeof a !== typeof b || a == null || b == null) return false;
36
+
37
+ if (Array.isArray(a)) {
38
+ if (!Array.isArray(b) || a.length !== b.length) return false;
39
+ return a.every((val, i) => isEqual(val, b[i]));
40
+ }
41
+
42
+ if (typeof a === 'object') {
43
+ const aKeys = Object.keys(a);
44
+ const bKeys = Object.keys(b);
45
+ if (aKeys.length !== bKeys.length) return false;
46
+
47
+ return aKeys.every(key => b.hasOwnProperty(key) && isEqual(a[key], b[key]));
48
+ }
49
+
50
+ return false;
51
+ }
52
+
package/tsconfig.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "exclude": ["node_modules", "dist"],
3
+ "external": ["react", "react-dom"],
4
+ "include": ["./"],
5
+ "files": ["./types/global.d.ts"],
6
+ "compilerOptions": {
7
+ "outDir": "dist",
8
+ "target": "ES6",
9
+ "module": "CommonJS",
10
+ "declaration": true,
11
+ "strict": true,
12
+ "jsx": "react-jsx",
13
+ "sourceMap": true,
14
+ "baseUrl": ".",
15
+ "paths": {
16
+ },
17
+ "rootDirs": ["./*"],
18
+ "esModuleInterop": true,
19
+ "skipLibCheck": true,
20
+ "forceConsistentCasingInFileNames": true,
21
+ "noImplicitAny": true,
22
+ "noImplicitThis": true,
23
+ "strictNullChecks": true
24
+ }
25
+ }
@@ -0,0 +1,7 @@
1
+ declare module '*.png'
2
+ declare module '*.svg'
3
+ declare module '*.jpeg'
4
+ declare module '*.jpg'
5
+ declare module '*.webp'
6
+ declare module '*.lottie'
7
+ declare module '*.json'