Skip to content

Commit

Permalink
feat: add initial featurebase integration
Browse files Browse the repository at this point in the history
TODO:
- [x] Allow client integrations to be lazy loaded
- [ ] Allow server integrations to be lazy loaded
  • Loading branch information
marcus-sa committed Jan 12, 2024
1 parent de3b1a1 commit f580900
Show file tree
Hide file tree
Showing 43 changed files with 606 additions and 74 deletions.
3 changes: 1 addition & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@
"@typescript-eslint/no-unsafe-call": "warn",
"@typescript-eslint/no-unsafe-assignment": "warn",
"@typescript-eslint/no-unsafe-return": "warn",

"@typescript-eslint/no-extraneous-class": "off",
"@typescript-eslint/require-await": "warn",
"@typescript-eslint/no-misused-promises": [
Expand All @@ -93,7 +92,7 @@
"checksVoidReturn": false
}
],
"@typescript-eslint/await-thenable": ["error"],
"@typescript-eslint/await-thenable": ["off"],
"@typescript-eslint/no-non-null-assertion": ["off"],
"@typescript-eslint/no-inferrable-types": ["off"],
"@typescript-eslint/explicit-module-boundary-types": ["warn"],
Expand Down
22 changes: 0 additions & 22 deletions apps/client/jest.config.ts

This file was deleted.

7 changes: 4 additions & 3 deletions apps/client/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,11 @@
"outputs": ["{options.outputFile}"]
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"executor": "@nx/vite:test",
"outputs": ["{options.reportsDirectory}"],
"options": {
"jestConfig": "apps/client/jest.config.ts"
"passWithNoTests": true,
"reportsDirectory": "../../coverage/{projectRoot}"
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions apps/client/src/app/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ export const appConfig: ApplicationConfig = {
provide: ApexClientConfig,
useValue: clientConfig,
},
importProvidersDynamicallyFrom(
await importProvidersDynamicallyFrom(
new IntegrationsModule(clientConfig.integrations),
),
{
provide: APP_INITIALIZER,
deps: [AuthService],
useFactory: (auth: AuthService) => () => auth.initialize(),
useFactory: (auth: AuthService) => async () => auth.initialize(),
multi: true,
},
],
Expand Down
27 changes: 27 additions & 0 deletions apps/client/src/app/integrations/integrations.module.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { test, vitest } from 'vitest';
import { TestBed } from '@angular/core/testing';
import { NgModule } from '@angular/core';

import { IntegrationsModule } from './integrations.module';

test('register', async () => {
// @ts-expect-error unnecessary
const integrations = new IntegrationsModule();
integrations.process = vitest.fn();

@NgModule({})
class TestModule {}

class TestConfig {}

const config = new TestConfig();

await integrations.register(config, () => TestModule);

const testBed = TestBed.configureTestingModule({
imports: [await integrations.configure()],
});

const config2 = testBed.inject(TestConfig);
expect(config2).toBe(config);
});
23 changes: 18 additions & 5 deletions apps/client/src/app/integrations/integrations.module.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,37 @@
import { ClassType } from '@deepkit/core';

import { DynamicNgModule, IntegrationsConfig } from '@apex/client';
import { SupabaseModule } from '@apex/integrations/supabase/client';

export class IntegrationsModule extends DynamicNgModule<IntegrationsModule> {
constructor(private readonly config: IntegrationsConfig) {
super();
}

private register(config: any, module: ClassType): void {
async register(
config: any,
loadModule: () => Promise<ClassType> | ClassType,
): Promise<void> {
if (config) {
this.addImport(module);
this.addImport(await loadModule());
this.addProvider({
provide: config.constructor,
useValue: config,
});
}
}

process(): void {
this.register(this.config.supabase, SupabaseModule);
async process(): Promise<void> {
await Promise.all([
this.register(this.config.supabase, async () =>
import('@apex/integrations/supabase/client').then(
m => m.SupabaseModule,
),
),
this.register(this.config.featurebase, async () =>
import('@apex/integrations/featurebase/client').then(
m => m.FeaturebaseModule,
),
),
]);
}
}
24 changes: 16 additions & 8 deletions apps/client/src/test-setup.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
// @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment
globalThis.ngJest = {
testEnvironmentOptions: {
errorOnUnknownElements: true,
errorOnUnknownProperties: true,
},
};
import 'jest-preset-angular/setup-jest';
import '@analogjs/vite-plugin-angular/setup-vitest';
import '@testing-library/jest-dom/vitest';

/**
* Initialize TestBed for all tests
*/
import { TestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';

TestBed.initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
);
4 changes: 2 additions & 2 deletions apps/client/tsconfig.app.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": []
"types": ["vite/client"]
},
"files": ["src/main.ts"],
"include": ["src/**/*.d.ts"],
"exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"]
"exclude": ["vite.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"]
}
2 changes: 1 addition & 1 deletion apps/client/tsconfig.editor.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"extends": "./tsconfig.json",
"include": ["src/**/*.ts"],
"compilerOptions": {
"types": ["jest", "node"]
"types": ["vitest/client", "vitest/globals", "node"]
}
}
6 changes: 2 additions & 4 deletions apps/client/tsconfig.spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"target": "es2016",
"types": ["jest", "node"]
"types": ["vitest/globals", "node"]
},
"files": ["src/test-setup.ts"],
"include": [
"jest.config.ts",
"vite.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.d.ts"
Expand Down
58 changes: 41 additions & 17 deletions apps/client/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,49 @@
import { join } from 'path';
import { join } from 'node:path';
import { defineConfig } from 'vite';
import { deepkitType } from '@deepkit/vite';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
import { angular } from '@analogjs/vite-plugin-angular/src/lib/angular-vite-plugin';

export default defineConfig({
optimizeDeps: {
exclude: ['@deepkit/rpc'],
esbuildOptions: {
export default defineConfig(({ mode }) => {
const isTestMode = mode === 'test';
const tsConfig = isTestMode ? join(__dirname, 'tsconfig.spec.json') : join(__dirname, 'tsconfig.app.json');

return {
optimizeDeps: {
exclude: ['@deepkit/rpc'],
esbuildOptions: {
target: 'esnext',
},
},
build: {
target: 'esnext',
},
},
build: {
target: 'esnext',
},
plugins: [
nxViteTsPaths(),
deepkitType({
compilerOptions: {
sourceMap: true,
plugins: [
nxViteTsPaths(),
isTestMode && angular({ tsconfig: tsConfig }),
deepkitType({
compilerOptions: {
sourceMap: true,
},
tsConfig,
}),
],
test: {
globals: true,
cache: {
dir: '../../node_modules/.vitest',
},
tsConfig: join(__dirname, 'tsconfig.app.json'),
}),
],
reporters: ['default'],
environment: 'jsdom',
setupFiles: ['src/test-setup.ts'],
include: ['src/**/*.spec.ts'],
deps: {
optimizer: {
web: {
exclude: ['rxjs', '@deepkit/rpc'],
},
}
},
},
};
});
9 changes: 9 additions & 0 deletions apps/server/src/integrations/integrations.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ import {
SupabaseConfig,
SupabaseModule,
} from '@apex/integrations/supabase/server';
import {
FeaturebaseConfig,
FeaturebaseModule,
} from '@apex/integrations/featurebase/server';

export class IntegrationsConfig {
readonly supabase?: SupabaseConfig;
readonly featurebase?: FeaturebaseConfig;
readonly thirdweb?: unknown;
}

Expand All @@ -18,5 +23,9 @@ export class IntegrationsModule extends createModule({
if (this.config.supabase) {
this.addImport(new SupabaseModule(this.config.supabase));
}

if (this.config.featurebase) {
this.addImport(new FeaturebaseModule(this.config.featurebase));
}
}
}
1 change: 1 addition & 0 deletions libs/api/shared/src/lib/entities/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export class User {
readonly inventory: Inventory & BackReference = new Inventory(this);
readonly rooms: readonly Room[] & BackReference = [];
readonly username: string & Unique;
readonly email?: string & Unique;
readonly look: string;
readonly online: boolean = false;
readonly friends: readonly Friend[] &
Expand Down
5 changes: 4 additions & 1 deletion libs/client/src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import { deserialize } from '@deepkit/type';

// FIXME: circular dependency
// import { SupabaseConfig } from '@apex/integrations/supabase/client';
// import { FeaturebaseConfig } from '@apex/integrations/featurebase/client';
// import { ThirdWebConfig } from '@apex/integrations/thirdweb/client';

export class IntegrationsConfig {
readonly supabase?: unknown; // SupabaseConfig
readonly thirdweb?: unknown;
readonly featurebase?: unknown; // Featurebase
readonly thirdweb?: unknown; // ThirdWebConfig
}

export class ApexClientConfig {
Expand Down
16 changes: 10 additions & 6 deletions libs/client/src/lib/dynamic-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,14 @@ import { setFactoryDef, setInjectorDef, setNgModuleDef } from './utils';

export type NgModuleProvider = Provider | EnvironmentProviders;

export function importProvidersDynamicallyFrom(
export async function importProvidersDynamicallyFrom(
...modules: readonly DynamicNgModule<unknown>[]
): EnvironmentProviders {
return importProvidersFrom(...modules.map(module => module.configure()));
): Promise<EnvironmentProviders> {
let configuredModules: readonly ModuleWithProviders<unknown>[] = [];
for (const module of modules) {
configuredModules = [...configuredModules, await module.configure()];
}
return importProvidersFrom(...configuredModules);
}

export abstract class DynamicNgModule<T> {
Expand All @@ -28,10 +32,10 @@ export abstract class DynamicNgModule<T> {
this.imports.add(module);
}

protected abstract process(): void;
protected abstract process(): Promise<void> | void;

configure(): ModuleWithProviders<T> {
this.process();
async configure(): Promise<ModuleWithProviders<T>> {
await this.process();

setNgModuleDef(this.constructor, {
type: this.constructor,
Expand Down
1 change: 1 addition & 0 deletions libs/integrations/featurebase/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# [Featurebase](https://featurebase.app) Integration
33 changes: 33 additions & 0 deletions libs/integrations/featurebase/client/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"extends": ["../../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"parserOptions": {
"project": ["libs/integrations/featurebase/client/tsconfig.*?.json"]
},
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": [
"error",
{
"ignoredFiles": ["{projectRoot}/rollup.config.{js,ts,mjs,mts}"]
}
]
}
}
]
}
11 changes: 11 additions & 0 deletions libs/integrations/featurebase/client/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable */
export default {
displayName: 'integrations-featurebase-client',
preset: '../../../../jest.preset.js',
transform: {
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
},
moduleFileExtensions: ['ts', 'js', 'html'],
testEnvironment: 'node',
coverageDirectory: '../../../../coverage/libs/integrations/featurebase/client',
};
Empty file.
Loading

0 comments on commit f580900

Please sign in to comment.