diff --git a/package.json b/package.json index aaf58ea1..6852eeb2 100644 --- a/package.json +++ b/package.json @@ -73,10 +73,10 @@ "@fullcalendar/moment": "^5.11.3", "@fullcalendar/moment-timezone": "^5.11.3", "@fullcalendar/timegrid": "^5.11.3", - "@rollup/plugin-commonjs": "^23.0.4", - "@rollup/plugin-multi-entry": "^6.0.0", - "@rollup/plugin-node-resolve": "^15.0.1", - "@rollup/plugin-typescript": "^9.0.2", + "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-multi-entry": "^6.0.1", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-typescript": "^11.1.5", "@stratusjs/angular": "file:packages/angular", "@stratusjs/angularjs": "file:packages/angularjs", "@stratusjs/angularjs-extras": "file:packages/angularjs-extras", @@ -120,8 +120,8 @@ "bowser": "^2.11.0", "codelyzer": "^6.0.2", "codemirror": "^6.0.1", - "core-js": "^3.19.0", - "css-loader": "^6.7.1", + "core-js": "^3.34.0", + "css-loader": "^6.8.1", "cssnano": "^5.1.14", "del": "^5.0.0", "dropzone": "^5.7.1", @@ -131,7 +131,7 @@ "glob": "^8.0.3", "gulp": "^4.0.2", "gulp-babel": "^8.0.0", - "gulp-clean-css": "^4.2.0", + "gulp-clean-css": "^4.3.0", "gulp-cli": "^2.3.0", "gulp-coffee": "^3.0.3", "gulp-concat": "^2.6.1", diff --git a/packages/angular/package.json b/packages/angular/package.json index 488d5d84..02c225f2 100644 --- a/packages/angular/package.json +++ b/packages/angular/package.json @@ -56,7 +56,7 @@ "@angular/platform-browser-dynamic": "^12.2.16", "@angular/router": "^12.2.16", "@codemirror/lang-html": "^6.4.0", - "@stratusjs/boot": "^0.4.0", + "@stratusjs/boot": "^1.2.0", "@stratusjs/core": "^0.6.2", "@stratusjs/runtime": "^0.12.1", "angular-froala-wysiwyg": "^3.2.7", @@ -65,6 +65,7 @@ "hammerjs": "^2.0.8", "html2pdf.js": "^0.9.2", "lodash": "^4.17.21", + "ngx-json-viewer": "^3.0.1", "reflect-metadata": "^0.1.13", "rxjs": "^6.6.7", "rxjs-compat": "^6.6.7", diff --git a/packages/angular/src/app.module.ts b/packages/angular/src/app.module.ts index 87333c77..ad0c2c1d 100644 --- a/packages/angular/src/app.module.ts +++ b/packages/angular/src/app.module.ts @@ -40,6 +40,7 @@ import {BaseComponent} from './base/base.component' // Stratus Custom Directives/Components import {ConfirmDialogComponent} from './confirm-dialog/confirm-dialog.component' import {EditorComponent} from './editor/editor.component' +import {JsonComponent} from './json/json.component' import {MediaSelectorComponent} from './media-selector/media-selector.component' import {SelectorComponent} from './selector/selector.component' import {TreeComponent} from './tree/tree.component' @@ -57,6 +58,11 @@ import { FroalaViewModule } from 'angular-froala-wysiwyg' +// ngx-json-viewer Module (Required by JSON) +import { + NgxJsonViewerModule +} from 'ngx-json-viewer' + // Editor Dialogs import {CitationDialogComponent} from './editor/citation-dialog.component' import { @@ -111,6 +117,7 @@ const ngModuleImports: any[] = [ HttpClientModule, MaterialModules, MatNativeDateModule, + NgxJsonViewerModule, ReactiveFormsModule, // SelectorComponent.forRoot() ] @@ -122,6 +129,7 @@ const ngDeclarations: any[] = [ CodeViewDialogComponent, ConfirmDialogComponent, EditorComponent, + JsonComponent, LinkDialogComponent, MediaDialogComponent, MediaSelectorComponent, @@ -138,6 +146,7 @@ const ngEntryComponents: any[] = [ CodeViewDialogComponent, ConfirmDialogComponent, EditorComponent, + JsonComponent, LinkDialogComponent, MediaDialogComponent, MediaSelectorComponent, @@ -150,6 +159,7 @@ const ngEntryComponents: any[] = [ const appModuleComponents = { 'sa-base': BaseComponent, 'sa-editor': EditorComponent, + 'sa-json': JsonComponent, 'sa-media-selector': MediaSelectorComponent, 'sa-selector': SelectorComponent, 'sa-tree': TreeComponent diff --git a/packages/angular/src/base/base.component.ts b/packages/angular/src/base/base.component.ts index d8c21035..610e1a4f 100644 --- a/packages/angular/src/base/base.component.ts +++ b/packages/angular/src/base/base.component.ts @@ -92,7 +92,7 @@ export class BaseComponent extends RootComponent implements OnInit, OnChanges { } ngOnInit() { - console.info('selector.ngOnInit') + console.info('base.ngOnInit') } ngOnChanges() { diff --git a/packages/angular/src/boot.ts b/packages/angular/src/boot.ts index d6fd213b..ef0cd3ad 100644 --- a/packages/angular/src/boot.ts +++ b/packages/angular/src/boot.ts @@ -33,6 +33,7 @@ function angularBoot() { // 'sa-base', 'sa-boot', // Forcibly Boot Angular. Useful if element is not directly on a page onLoad 'sa-editor', + 'sa-json', 'sa-map', 'sa-media-selector', 'sa-selector', diff --git a/packages/angular/src/editor/editor.component.ts b/packages/angular/src/editor/editor.component.ts index 6eb9d5e2..757c4dd9 100644 --- a/packages/angular/src/editor/editor.component.ts +++ b/packages/angular/src/editor/editor.component.ts @@ -733,6 +733,7 @@ export class EditorComponent extends RootComponent implements OnInit, TriggerInt // Stratus Angular+ Tags 'sa-boot', 'sa-editor', + 'sa-json', 'sa-map', 'sa-media-selector', 'sa-selector', @@ -837,6 +838,7 @@ export class EditorComponent extends RootComponent implements OnInit, TriggerInt // Stratus Angular+ Tags 'sa-boot', 'sa-editor', + 'sa-json', 'sa-map', 'sa-media-selector', 'sa-selector', diff --git a/packages/angular/src/json/json.component.html b/packages/angular/src/json/json.component.html new file mode 100644 index 00000000..83aa2d7b --- /dev/null +++ b/packages/angular/src/json/json.component.html @@ -0,0 +1,2 @@ +Loaded! + diff --git a/packages/angular/src/json/json.component.scss b/packages/angular/src/json/json.component.scss new file mode 100644 index 00000000..01a8e2f1 --- /dev/null +++ b/packages/angular/src/json/json.component.scss @@ -0,0 +1,3 @@ +sa-json { + // stub for now +} diff --git a/packages/angular/src/json/json.component.ts b/packages/angular/src/json/json.component.ts new file mode 100644 index 00000000..71fce1ce --- /dev/null +++ b/packages/angular/src/json/json.component.ts @@ -0,0 +1,400 @@ +// NOTE: to register a component you need to add it to: +// 1. /packages/angular/src/boot.ts +// - This is where we must register if a component will be used in html, e.g. sa-selector +// - we are using a component as an Angular app, this allows us to have as many angular components on a page defined +// dynamically. +// 2. /packages/angular/src/app.module.ts +// - This is where we register every component that will be used or imported +// - add an import to define where it is located, e.g. import { JsonComponent } from './json.component' +// - add to declarations and entryComponents + +// Angular Core +import { + // ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnChanges, + OnInit +} from '@angular/core' +import { + DomSanitizer +} from '@angular/platform-browser' + +// Angular Material +// import {MatIconRegistry} from '@angular/material/icon' + +// External Dependencies +import {Stratus} from '@stratusjs/runtime/stratus' +import _, { has } from 'lodash' +import {cookie} from '@stratusjs/core/environment' + +// Stratus Angular Core +import {RootComponent} from '../core/root.component' + +// Transformers +import {keys} from 'ts-transformer-keys' +import {Observable, ObservableInput, Subject, Subscriber, timer} from 'rxjs' +import {FormBuilder, FormControl, FormGroup} from '@angular/forms' +import {MatDialog} from '@angular/material/dialog' +import {EventManager} from '@stratusjs/core/events/eventManager' +import {catchError, debounce} from 'rxjs/operators' +import {Registry} from '@stratusjs/angularjs/services/registry' +import {Collection} from '@stratusjs/angularjs/services/collection' +import {Model} from '@stratusjs/angularjs/services/model' +import {EventBase} from '@stratusjs/core/events/eventBase' + +// Local Setup +const systemDir = '@stratusjs/angular' +const moduleName = 'json' + +// Directory Template +const min = !cookie('env') ? '.min' : '' +const localDir = `${Stratus.BaseUrl}${boot.configuration.paths[`${systemDir}/*`].replace(/[^/]*$/, '').replace(/\/dist\/$/, '/src/')}` + +/** + * @title Json Viewer + */ +@Component({ + selector: 'sa-json', + // template: '', + templateUrl: `${localDir}/${moduleName}/${moduleName}.component${min}.html`, + // styleUrls: [`${localDir}/json/json.component${min}.css`], + // viewProviders: [JsonComponent] + // changeDetection: ChangeDetectionStrategy.OnPush +}) +// @Injectable() +export class JsonComponent extends RootComponent implements OnInit { + + // Basic Component Settings + title = `${moduleName}_component` + uid: string + dev = !!cookie('env') + debug = !!cookie(`debug_${moduleName}`) + + // Registry Attributes + @Input() target: string + @Input() targetSuffix: string + @Input() id: number + @Input() manifest: boolean + @Input() decouple: boolean + @Input() direct: boolean + @Input() api: object + @Input() urlRoot: string + + // Component Attributes + @Input() type: string + @Input() property: string + @Input() endpoint: string + + // Dependencies + _ = _ + has = has + log = console.log + Stratus = Stratus + + // Stratus Data Connectivity + registry = new Registry() + fetched: Promise + data: any + collection?: EventBase + // @Output() model: any; + model?: Model + + // Observable Connection + dataSub: Observable<[]> + onChange = new Subject() + subscriber: Subscriber + // Note: It may be better to LifeCycle::tick(), but this works for now + + // Icon Localization + svgIcons: { + [key: string]: string + } = {} + + // UI Flags + initialized = false + styled = false + blurred = false + focused = false + codeViewIsOpen: boolean + // disableRefresh = true + + // Forms + form: FormGroup = this.fb.group({ + dataString: new FormControl(), + }) + dataChangeLog: string[] = [] + incomingData = '' + outgoingData = '' + + // Debounce Saving Controls + debounceSave = true + debounceTime = 5000 + + // Model Saving Controls + forceSave = false + + constructor( + // private iconRegistry: MatIconRegistry, + private sanitizer: DomSanitizer, + protected ref: ChangeDetectorRef, + private elementRef: ElementRef, + private fb: FormBuilder, + public dialog: MatDialog, + ) { + // Chain constructor + super() + + // Initialization + this.uid = _.uniqueId(`sa_${_.snakeCase(moduleName)}_component_`) + Stratus.Instances[this.uid] = this + + // TODO: Assess & Possibly Remove when the System.js ecosystem is complete + // Load Component CSS until System.js can import CSS properly. + Stratus.Internals.LoadCss([ + `${localDir}${moduleName}/${moduleName}.component${min}.css`, + ]).then(() => { + this.styled = true + this.refresh() + }).catch((e: Error) => { + console.error(e) + console.warn('Issue detected in CSS Loader for Component:', this.uid) + this.styled = true + this.refresh() + }) + + // _.forEach(cssFiles, (file: string) => Stratus.Internals.CssLoader(file).catch((e) => console.error(e))) + + // Hydrate Root App Inputs + this.hydrate(elementRef, sanitizer, keys()) + + // Data Connections + // TODO: Spelunk through this code to determine why I am getting an empty component on occasion... + this.fetchData() + .then(data => { + if (!data || !(data instanceof EventManager)) { + console.warn('Unable to bind data from Registry!') + return + } + // Manually render upon model change + // this.ref.detach(); + const onDataChange = () => { + if (!data.completed) { + return + } + // this.onDataChange(); + this.dataDefer(this.subscriber) + // TODO: Add a debounce so we don't attempt to update multiple times while the model is changing. + // this.refresh() + // FIXME: Somehow this doesn't completely work... It gets data from the model + // when it is changed, but won't propagate it when the form event changes the data. + } + data.on('change', onDataChange) + onDataChange() + }) + + // Declare Observable with Subscriber (Only Happens Once) + // TODO: Test if the observable is necessary in any way... + this.dataSub = new Observable(subscriber => { + if (this.dev) { + console.warn(`[observable] creating subscriber on ${this.uid}`, subscriber) + } + return this.dataDefer(subscriber) + }) + this.dataSub.pipe( + // debounceTime(250), + debounce(() => timer(250)), + catchError(this.handleError) + ).subscribe(evt => { + // While the editor is focused, we skip debounce updates to avoid cursor glitches + if (this.focused) { + if (this.dev) { + console.warn(`[subscriber] waiting on updates due to focus on ${this.uid}`) + } + return + } + // TODO: This may need to only work on blur and not focus, unless it is the initialization value + const dataControl = this.form.get('dataString') + if (dataControl.value === evt) { + // In the case of data being edited by the code view or something else, + // we need to refresh the UI, as long as it has been initialized. + if (this.initialized) { + this.refresh() + } + return + } + dataControl.patchValue(evt) + // Note: A refresh may be necessary if things become less responsive + this.refresh() + }) + + // console.info('constructor!'); + } + + ngOnInit() { + this.initialized = true + // console.info(`${moduleName}.ngOnInit`) + const dataControl = this.form.get('dataString') + // This valueChanges field is an Event Emitter + if (this.debounceSave) { + // Pipe for Model Debounce + dataControl.valueChanges.pipe( + debounce(() => timer(this.debounceTime)), + catchError(this.handleError) + ).subscribe((evt: string) => this.modelSave(evt)) + // Pipe for changedExternal + dataControl.valueChanges.pipe( + debounce(() => timer(250)), + catchError(this.handleError) + ).subscribe((evt: string) => { + if (this.model.changedExternal) { + return + } + this.model.changedExternal = true + this.model.trigger('change') + }) + // Pipe for outgoingData + dataControl.valueChanges.forEach( + (value: string) => this.outgoingData = value + ) + } else { + dataControl.valueChanges.forEach( + (value: string) => this.modelSave(value) + ) + } + } + + // ngOnChanges() { + // // Display Inputs + // if (!this.dev) { + // return + // } + // console.log('inputs:', { + // target: this.target, + // targetSuffix: this.targetSuffix, + // id: this.id, + // manifest: this.manifest, + // decouple: this.decouple, + // direct: this.direct, + // api: this.api, + // urlRoot: this.urlRoot, + // }) + // } + + // ngDoCheck(): void { + // console.info('ngDoCheck:', this.dataSub); + // } + + handleError(err: ObservableInput): ObservableInput { + console.error(err) + return err + } + + // Data Connections + fetchData() { + if (this.fetched) { + return this.fetched + } + return this.fetched = this.registry.fetch( + Stratus.Select(this.elementRef.nativeElement), + this + ) + // return this.fetched = this.registry.fetch({ + // target: this.target, + // targetSuffix: this.targetSuffix, + // id: this.id, + // manifest: this.manifest, + // decouple: this.decouple, + // direct: this.direct, + // api: this.api, + // urlRoot: this.urlRoot, + // }, this) + } + + // Ensures Data is populated before hitting the Subscriber + dataDefer(subscriber: Subscriber) { + this.subscriber = this.subscriber || subscriber + if (!this.subscriber) { + if (this.dev) { + console.warn(`[defer] debouncing due to empty subscriber on ${this.uid}`) + } + setTimeout(() => { + if (this.dev) { + console.warn(`[defer] debounced subscriber returned on ${this.uid}`) + } + this.dataDefer(subscriber) + }, 250) + return + } + const prevString = _.clone(this.incomingData) + const dataString = this.dataRef() + // ensure changes have occurred + if (prevString === dataString) { + return + } + if (!dataString && (!this.data || !this.data.completed)) { + if (this.dev) { + console.warn(`[defer] debouncing subscriber due to unavailable data on ${this.uid}`, this.data) + } + setTimeout(() => { + if (this.dev) { + console.warn(`[defer] debounced subscriber returned on ${this.uid}`) + } + this.dataDefer(subscriber) + }, 250) + return + } + if (prevString === dataString) { + return + } + if (this.dev) { + console.warn(`[subscriber] new value submitted on ${this.uid}:`, dataString) + } + this.subscriber.next(dataString) + // TODO: Add a returned Promise to ensure async/await can use this defer directly. + } + + dataRef(): string { + if (!this.model) { + return '' + } + return this.incomingData = this.model.get(this.property) + } + + onDataChange() { + // FIXME: This is not in use due to contextual issues. + this.dataDefer(this.subscriber) + this.refresh() + } + + modelSave(value: string) { + // Avoid saving until the Model is truly available + if (!this.model.completed) { + return + } + + // This avoids saving if it's the same + // if (value === this.model.get(this.property)) { + // return + // } + + // This keeps a change log of what's been typed. I used this for testing purposes, + // but something this simple could be used for simple UX purposes down the road. + // this.dataChangeLog.push(value) + + // Save the qualified change! + this.model.set(this.property, value) + + // Remove the changedExternal flag if using debounceSave + if (this.debounceSave) { + this.model.changedExternal = false + } + + // If enabled, Force Save on Persisted Models + if (this.forceSave && !_.isEmpty(this.model.getIdentifier())) { + this.model.save() + } + } +} diff --git a/packages/angular/src/selector/selector.component.ts b/packages/angular/src/selector/selector.component.ts index 9efc44aa..aa267ea0 100644 --- a/packages/angular/src/selector/selector.component.ts +++ b/packages/angular/src/selector/selector.component.ts @@ -280,7 +280,7 @@ export class SelectorComponent extends RootComponent { // implements OnInit, OnC }) xhr.send() .then((response: LooseObject | Array | string) => { - if (!_.isObject(response) || _.get(response, 'meta.status[0].code') !== 'SUCCESS') { + if (!_.isObject(response) || (_.has(response, 'meta.success') && !_.get(response, 'meta.success'))) { console.error('error[toggleStatus]:', response) model.status = statusOriginal this.refresh() diff --git a/packages/calendar/package.json b/packages/calendar/package.json index 503f5833..e45b495c 100644 --- a/packages/calendar/package.json +++ b/packages/calendar/package.json @@ -36,7 +36,7 @@ "@fullcalendar/timegrid": "^5.11.2", "@stratusjs/angularjs": "^0.6.2", "@stratusjs/angularjs-extras": "^0.12.12", - "@stratusjs/boot": "^1.0.1", + "@stratusjs/boot": "^1.2.0", "@stratusjs/runtime": "^0.12.1", "ical.js": "^1.5.0", "tslib": "^2.4.0" diff --git a/packages/idx/package.json b/packages/idx/package.json index 2ac870de..16271dcf 100644 --- a/packages/idx/package.json +++ b/packages/idx/package.json @@ -33,7 +33,7 @@ "@stratusjs/angular": "^0.8.3", "@stratusjs/angularjs": "^0.7.1", "@stratusjs/angularjs-extras": "^0.13.0", - "@stratusjs/boot": "^1.1.0", + "@stratusjs/boot": "^1.2.0", "@stratusjs/map": "^0.6.5", "@stratusjs/runtime": "^0.12.1", "@stratusjs/swiper": "^1.2.2" diff --git a/packages/swiper/package.json b/packages/swiper/package.json index ce134284..1b3aa7e7 100644 --- a/packages/swiper/package.json +++ b/packages/swiper/package.json @@ -30,7 +30,7 @@ "dependencies": { "@stratusjs/angularjs": "^0.6.4", "@stratusjs/angularjs-extras": "^0.13.0", - "@stratusjs/boot": "^1.1.0", + "@stratusjs/boot": "^1.2.0", "@stratusjs/runtime": "^0.12.1", "swiper": "^10.3.0" } diff --git a/rollup.config.js b/rollup.config.js index cbca53f1..8944fe62 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -2,8 +2,12 @@ import multi from '@rollup/plugin-multi-entry' import { nodeResolve } from '@rollup/plugin-node-resolve' // import commonjs from '@rollup/plugin-commonjs' +import typescript from '@rollup/plugin-typescript' import postcss from 'rollup-plugin-postcss' +// TypeScript Transformers +import { default as keysTransformer } from 'ts-transformer-keys/transformer' + export default [ // ------------------------ // Core Config @@ -39,43 +43,58 @@ export default [ // ------------------------ // This includes all things in app.module.ts like the Map Package, Stripe, etc { - input: { - include: [ - './packages/angular/src/**/*.js' - ], - exclude: [ - // min files - './packages/angular/src/**/*.min.js', - // test files - './packages/angular/src/test/**/*.js', - // unused - './packages/angular/src/quill-plugins/**/*.js', - // prototype file (not functional) - './packages/angular/src/angular.module.js', - // boot controls angular detection, so it shouldn't be packages - './packages/angular/src/boot.js' - ] - }, + input: './packages/angular/src/main.ts', external: [ 'froala-editor', 'html2pdf', 'lodash', 'toastify-js', + '@stratusjs', + '@stratusjs/angularjs', + '@stratusjs/angularjs-extras', + '@stratusjs/backend', + '@stratusjs/boot', + '@stratusjs/calendar', + '@stratusjs/core', + '@stratusjs/react', + '@stratusjs/runtime', + '@stratusjs/swiper', + 'angular', + 'angular-material', + 'angular-sanitize', 'zone.js/dist/zone', // TODO: This one may be a transformer, so it shouldn't need to be imported. '@agentepsilon/decko' ], output: { - // file: 'packages/angular/dist/angular.bundle.js', - dir: 'packages/angular/dist/', + file: 'packages/angular/dist/angular.bundle.js', format: 'system' }, plugins: [ - multi({ - exports: true, - entryFileName: 'angular.bundle.js' + // angular({ + // replace:false, + // preprocessors: { + // template: template => minify(template, { + // caseSensitive: true, // Angular+ uses case sensitive templates + // collapseWhitespace: true, + // removeComments: true, + // removeEmptyAttributes: true + // }), + // // style: scss => compile(scss), + // } + // }), + nodeResolve(), + typescript({ + tsconfig: './tsconfig.rollup.json', + transformers: { + before: [ + { + type: 'program', + factory: (program) => keysTransformer(program) + } + ] + } }), - nodeResolve() ] }, // ------------------------ diff --git a/tsconfig.rollup.json b/tsconfig.rollup.json new file mode 100644 index 00000000..04b5f4ad --- /dev/null +++ b/tsconfig.rollup.json @@ -0,0 +1,81 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "ES2020", + "moduleResolution": "node", + "noImplicitAny": true, + "removeComments": true, + "preserveConstEnums": true, + "inlineSourceMap": false, + "downlevelIteration": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "esModuleInterop": true, + "skipLibCheck": true, + "baseUrl": "", + "typeRoots": [ + "node_modules/@types" + ], + "lib": [ + "es2019", + "dom" + ], + "paths": { + "*": [ + "node_modules/*" + ], +// "@stratusjs/*": [ +// "packages/*" +// ], + "@stratusjs/angular/*": [ + "packages/angular/src/*" + ], + "@stratusjs/angularjs/*": [ + "packages/angularjs/src/*" + ], + "@stratusjs/angularjs-extras/*": [ + "packages/angularjs-extras/src/*" + ], + "@stratusjs/backend/*": [ + "packages/backend/src/*" + ], + "@stratusjs/boot/*": [ + "packages/boot/src/*" + ], + "@stratusjs/calendar/*": [ + "packages/calendar/src/*" + ], + "@stratusjs/core/*": [ + "packages/core/src/*" + ], + "@stratusjs/idx/*": [ + "packages/idx/src/*" + ], + "@stratusjs/map/*": [ + "packages/map/src/*" + ], + "@stratusjs/react/*": [ + "packages/react/src/*" + ], + "@stratusjs/runtime/*": [ + "packages/runtime/src/*" + ], + "@stratusjs/stripe/*": [ + "packages/stripe/src/*" + ], + "@stratusjs/swiper/*": [ + "packages/swiper/src/*" + ], + "stratus": [ + "packages/runtime/src/stratus" + ] + }, + "jsx": "react" + }, + "exclude": [ + "node_modules" + ], + "include": [ + "./packages/*/src/**/*.ts" + ] +}