From 40bcf76224dbc6918d2cf8baca596791a455b3cb Mon Sep 17 00:00:00 2001 From: Thomas Schaffter Date: Fri, 7 Jun 2024 21:19:17 -0700 Subject: [PATCH] feat(model-ad): enable SSR (#2701) --- apps/model-ad/app/project.json | 42 +++++++- apps/model-ad/app/server.ts | 65 +++++++++--- apps/model-ad/app/tsconfig.app.json | 21 +++- apps/model-ad/app/tsconfig.json | 5 +- apps/model-ad/app/tsconfig.server.json | 16 +++ apps/openchallenges/app/server.ts.bak | 97 ------------------ apps/openchallenges/app/tsconfig.json | 2 +- .../not-found/src/lib/not-found.component.ts | 12 ++- libs/model-ad/not-found/src/lib/seo-data.ts | 8 ++ libs/model-ad/util/.eslintrc.json | 50 ++++++++++ libs/model-ad/util/README.md | 7 ++ libs/model-ad/util/jest.config.ts | 22 +++++ libs/model-ad/util/project.json | 27 +++++ libs/model-ad/util/src/index.ts | 1 + .../model-ad/util/src/lib/default-seo-data.ts | 14 +++ libs/model-ad/util/src/test-setup.ts | 1 + libs/model-ad/util/tsconfig.json | 26 +++++ libs/model-ad/util/tsconfig.lib.json | 12 +++ libs/model-ad/util/tsconfig.spec.json | 10 ++ tsconfig.base.json | 99 ++++++++++++++----- 20 files changed, 387 insertions(+), 150 deletions(-) create mode 100644 apps/model-ad/app/tsconfig.server.json delete mode 100644 apps/openchallenges/app/server.ts.bak create mode 100644 libs/model-ad/not-found/src/lib/seo-data.ts create mode 100644 libs/model-ad/util/.eslintrc.json create mode 100644 libs/model-ad/util/README.md create mode 100644 libs/model-ad/util/jest.config.ts create mode 100644 libs/model-ad/util/project.json create mode 100644 libs/model-ad/util/src/index.ts create mode 100644 libs/model-ad/util/src/lib/default-seo-data.ts create mode 100644 libs/model-ad/util/src/test-setup.ts create mode 100644 libs/model-ad/util/tsconfig.json create mode 100644 libs/model-ad/util/tsconfig.lib.json create mode 100644 libs/model-ad/util/tsconfig.spec.json diff --git a/apps/model-ad/app/project.json b/apps/model-ad/app/project.json index 971d1e901b..9997b6c1cb 100644 --- a/apps/model-ad/app/project.json +++ b/apps/model-ad/app/project.json @@ -96,12 +96,44 @@ "jestConfig": "apps/model-ad/app/jest.config.ts" } }, - "serve-static": { - "executor": "@nx/web:file-server", + "server": { + "dependsOn": [ + "build" + ], + "executor": "@angular-devkit/build-angular:server", "options": { - "buildTarget": "model-ad-app:build", - "staticFilePath": "dist/apps/model-ad/app/browser" - } + "outputPath": "dist/apps/model-ad/app/browser/server", + "main": "apps/model-ad/app/server.ts", + "tsConfig": "apps/model-ad/app/tsconfig.server.json", + "inlineStyleLanguage": "scss" + }, + "configurations": { + "production": { + "outputHashing": "media" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "sourceMap": true, + "extractLicenses": false, + "vendorChunk": true + } + }, + "defaultConfiguration": "production" + }, + "serve-ssr": { + "executor": "@angular-devkit/build-angular:ssr-dev-server", + "configurations": { + "development": { + "browserTarget": "model-ad-app:build:development", + "serverTarget": "model-ad-app:server:development" + }, + "production": { + "browserTarget": "model-ad-app:build:production", + "serverTarget": "model-ad-app:server:production" + } + }, + "defaultConfiguration": "development" } }, "implicitDependencies": [ diff --git a/apps/model-ad/app/server.ts b/apps/model-ad/app/server.ts index b1493d0eae..3d19aabd33 100644 --- a/apps/model-ad/app/server.ts +++ b/apps/model-ad/app/server.ts @@ -1,32 +1,44 @@ +import 'zone.js/node'; + import { APP_BASE_HREF } from '@angular/common'; import { CommonEngine } from '@angular/ssr'; -import express from 'express'; -import { fileURLToPath } from 'node:url'; -import { dirname, join, resolve } from 'node:path'; +import * as express from 'express'; +import { existsSync } from 'node:fs'; +import { join } from 'node:path'; import bootstrap from './src/main.server'; +const PORT = process.env['PORT'] || '4200'; +console.log(`server.ts: ${PORT}`); + // The Express app is exported so that it can be used by serverless Functions. export function app(): express.Express { const server = express(); - const serverDistFolder = dirname(fileURLToPath(import.meta.url)); - const browserDistFolder = resolve(serverDistFolder, '../browser'); - const indexHtml = join(serverDistFolder, 'index.server.html'); + const distFolder = join( + process.cwd(), + 'dist/apps/model-ad/app/browser/browser' + ); + const indexHtml = existsSync(join(distFolder, 'index.original.html')) + ? join(distFolder, 'index.original.html') + : join(distFolder, 'index.html'); const commonEngine = new CommonEngine(); server.set('view engine', 'html'); - server.set('views', browserDistFolder); + server.set('views', distFolder); // Example Express Rest API endpoints // server.get('/api/**', (req, res) => { }); // Serve static files from /browser server.get( '*.*', - express.static(browserDistFolder, { + express.static(distFolder, { maxAge: '1y', }) ); + // Health endpoint used by the container + server.get('/health', (_req, res) => res.status(200).json({ status: 'UP' })); + // All regular routes use the Angular engine server.get('*', (req, res, next) => { const { protocol, originalUrl, baseUrl, headers } = req; @@ -36,8 +48,22 @@ export function app(): express.Express { bootstrap, documentFilePath: indexHtml, url: `${protocol}://${headers.host}${originalUrl}`, - publicPath: browserDistFolder, - providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }], + publicPath: distFolder, + providers: [ + { provide: APP_BASE_HREF, useValue: baseUrl }, + // The base URL enables the app to load the app config file during server-side rendering. + { + provide: 'APP_BASE_URL', + // the format of ${host} is `host:port` + useFactory: () => `${protocol}://${headers.host}`, + deps: [], + }, + { + provide: 'APP_PORT', + useValue: PORT, + deps: [], + }, + ], }) .then((html) => res.send(html)) .catch((err) => next(err)); @@ -47,13 +73,22 @@ export function app(): express.Express { } function run(): void { - const port = process.env['PORT'] || 4000; - // Start up the Node server const server = app(); - server.listen(port, () => { - console.log(`Node Express server listening on http://localhost:${port}`); + server.listen(PORT, () => { + console.log(`Node Express server listening on http://localhost:${PORT}`); }); } -run(); +// Webpack will replace 'require' with '__webpack_require__' +// '__non_webpack_require__' is a proxy to Node 'require' +// The below code is to ensure that the server is run only when not requiring the bundle. +/* eslint-disable camelcase,no-undef */ +declare const __non_webpack_require__: NodeRequire; +const mainModule = __non_webpack_require__.main; +const moduleFilename = (mainModule && mainModule.filename) || ''; +if (moduleFilename === __filename || moduleFilename.includes('iisnode')) { + run(); +} + +export default bootstrap; diff --git a/apps/model-ad/app/tsconfig.app.json b/apps/model-ad/app/tsconfig.app.json index 2afe01ea4a..a979b5683d 100644 --- a/apps/model-ad/app/tsconfig.app.json +++ b/apps/model-ad/app/tsconfig.app.json @@ -2,9 +2,20 @@ "extends": "./tsconfig.json", "compilerOptions": { "outDir": "../../../dist/out-tsc", - "types": ["node"] + "types": [ + "node" + ] }, - "files": ["src/main.ts", "src/main.server.ts", "server.ts"], - "include": ["src/**/*.d.ts"], - "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] -} + "files": [ + "src/main.ts", + "src/main.server.ts", + ], + "include": [ + "src/**/*.d.ts" + ], + "exclude": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + ] +} \ No newline at end of file diff --git a/apps/model-ad/app/tsconfig.json b/apps/model-ad/app/tsconfig.json index 4383e7eb81..f12e9f7034 100644 --- a/apps/model-ad/app/tsconfig.json +++ b/apps/model-ad/app/tsconfig.json @@ -21,6 +21,9 @@ }, { "path": "./tsconfig.spec.json" + }, + { + "path": "./tsconfig.server.json" } ], "extends": "../../../tsconfig.base.json", @@ -30,4 +33,4 @@ "strictInputAccessModifiers": true, "strictTemplates": true } -} +} \ No newline at end of file diff --git a/apps/model-ad/app/tsconfig.server.json b/apps/model-ad/app/tsconfig.server.json new file mode 100644 index 0000000000..6edb093e23 --- /dev/null +++ b/apps/model-ad/app/tsconfig.server.json @@ -0,0 +1,16 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.app.json", + "compilerOptions": { + "outDir": "../../out-tsc/server", + "target": "ES2022", + "types": [ + "node" + ], + "esModuleInterop": false, + }, + "files": [ + "src/main.server.ts", + "server.ts" + ] +} \ No newline at end of file diff --git a/apps/openchallenges/app/server.ts.bak b/apps/openchallenges/app/server.ts.bak deleted file mode 100644 index cc7992a250..0000000000 --- a/apps/openchallenges/app/server.ts.bak +++ /dev/null @@ -1,97 +0,0 @@ -import 'zone.js/dist/zone-node'; - -import { APP_BASE_HREF } from '@angular/common'; -import { ngExpressEngine } from '@nguniversal/express-engine'; -import * as express from 'express'; -import { existsSync } from 'fs'; -import { join } from 'path'; - -import bootstrap from './src/main.server'; - -// that process port comes from the above builder utils -// About setting a constant value: https://github.com/angular/universal/issues/1628 -const PORT = process.env['PORT'] || '4200'; -console.log(`server.ts: ${PORT}`); - -// The Express app is exported so that it can be used by serverless Functions. -export function app(): express.Express { - const server = express(); - const distFolder = join( - process.cwd(), - 'dist/apps/openchallenges/app/browser/browser' - ); - const indexHtml = existsSync(join(distFolder, 'index.original.html')) - ? 'index.original.html' - : 'index'; - - // Our Universal express-engine (found @ https://github.com/angular/universal/tree/main/modules/express-engine) - server.engine( - 'html', - ngExpressEngine({ - bootstrap, - }) - ); - - server.set('view engine', 'html'); - server.set('views', distFolder); - - // Example Express Rest API endpoints - // server.get('/api/**', (req, res) => { }); - // Serve static files from /browser - server.get( - '*.*', - express.static(distFolder, { - maxAge: '1y', - }) - ); - - // Health endpoint used by the container - server.get('/health', (_req, res) => res.status(200).json({ status: 'UP' })); - - // All regular routes use the Universal engine - server.get('*', (req, res) => { - const protocol = req.protocol; - const host = req.get('host'); - res.render(indexHtml, { - req, - providers: [ - { provide: APP_BASE_HREF, useValue: req.baseUrl }, - // The base URL enables the app to load the app config file during server-side rendering. - { - provide: 'APP_BASE_URL', - // the format of ${host} is `host:port` - useFactory: () => `${protocol}://${host}`, - deps: [], - }, - { - provide: 'APP_PORT', - useValue: PORT, - deps: [], - }, - ], - }); - }); - - return server; -} - -function run(): void { - // Start up the Node server - const server = app(); - server.listen(PORT, () => { - console.log(`Node Express server listening on http://localhost:${PORT}`); - }); -} - -// Webpack will replace 'require' with '__webpack_require__' -// '__non_webpack_require__' is a proxy to Node 'require' -// The below code is to ensure that the server is run only when not requiring the bundle. -/* eslint-disable camelcase,no-undef */ -declare const __non_webpack_require__: NodeRequire; -const mainModule = __non_webpack_require__.main; -const moduleFilename = (mainModule && mainModule.filename) || ''; -if (moduleFilename === __filename || moduleFilename.includes('iisnode')) { - run(); -} - -export default bootstrap; diff --git a/apps/openchallenges/app/tsconfig.json b/apps/openchallenges/app/tsconfig.json index e85865cf5b..2707c7916a 100644 --- a/apps/openchallenges/app/tsconfig.json +++ b/apps/openchallenges/app/tsconfig.json @@ -29,4 +29,4 @@ "strictInputAccessModifiers": true, "strictTemplates": true } -} +} \ No newline at end of file diff --git a/libs/model-ad/not-found/src/lib/not-found.component.ts b/libs/model-ad/not-found/src/lib/not-found.component.ts index 980fd1b4dc..937058235b 100644 --- a/libs/model-ad/not-found/src/lib/not-found.component.ts +++ b/libs/model-ad/not-found/src/lib/not-found.component.ts @@ -1,10 +1,12 @@ import { CommonModule } from '@angular/common'; -import { Component } from '@angular/core'; +import { Component, Renderer2 } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { RouterModule } from '@angular/router'; import { MatCardModule } from '@angular/material/card'; import { FooterComponent } from '@sagebionetworks/model-ad/ui'; import { ConfigService } from '@sagebionetworks/model-ad/config'; +import { SeoService } from '@sagebionetworks/shared/util'; +import { getSeoData } from './seo-data'; @Component({ selector: 'model-ad-not-found', @@ -26,11 +28,17 @@ export class NotFoundComponent { public termsOfUseUrl: string; public apiDocsUrl: string; - constructor(private readonly configService: ConfigService) { + constructor( + private readonly configService: ConfigService, + private seoService: SeoService, + private renderer2: Renderer2 + ) { this.appVersion = this.configService.config.appVersion; this.dataUpdatedOn = this.configService.config.dataUpdatedOn; this.privacyPolicyUrl = this.configService.config.privacyPolicyUrl; this.termsOfUseUrl = this.configService.config.termsOfUseUrl; this.apiDocsUrl = this.configService.config.apiDocsUrl; + + this.seoService.setData(getSeoData(), this.renderer2); } } diff --git a/libs/model-ad/not-found/src/lib/seo-data.ts b/libs/model-ad/not-found/src/lib/seo-data.ts new file mode 100644 index 0000000000..3d1caa8ebe --- /dev/null +++ b/libs/model-ad/not-found/src/lib/seo-data.ts @@ -0,0 +1,8 @@ +import { SeoData } from '@sagebionetworks/shared/util'; +import { getDefaultSeoData } from '@sagebionetworks/model-ad/util'; + +export const getSeoData = (): SeoData => { + return Object.assign(getDefaultSeoData(), { + title: 'Not Found | MODEL-AD', + } as SeoData); +}; diff --git a/libs/model-ad/util/.eslintrc.json b/libs/model-ad/util/.eslintrc.json new file mode 100644 index 0000000000..ca19a6a1fd --- /dev/null +++ b/libs/model-ad/util/.eslintrc.json @@ -0,0 +1,50 @@ +{ + "extends": [ + "../../../.eslintrc.json" + ], + "ignorePatterns": [ + "!**/*" + ], + "env": { + "jest": true + }, + "overrides": [ + { + "files": [ + "*.ts" + ], + "extends": [ + "plugin:@nx/angular", + "plugin:@angular-eslint/template/process-inline-templates", + "plugin:jest/recommended" + ], + "rules": { + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "modelAd", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "model-ad", + "style": "kebab-case" + } + ] + } + }, + { + "files": [ + "*.html" + ], + "extends": [ + "plugin:@nx/angular-template" + ], + "rules": {} + } + ] +} \ No newline at end of file diff --git a/libs/model-ad/util/README.md b/libs/model-ad/util/README.md new file mode 100644 index 0000000000..f8a0534b33 --- /dev/null +++ b/libs/model-ad/util/README.md @@ -0,0 +1,7 @@ +# web-util + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test web-util` to execute the unit tests. diff --git a/libs/model-ad/util/jest.config.ts b/libs/model-ad/util/jest.config.ts new file mode 100644 index 0000000000..e891a8e70c --- /dev/null +++ b/libs/model-ad/util/jest.config.ts @@ -0,0 +1,22 @@ +module.exports = { + displayName: 'web-util', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + globals: {}, + coverageDirectory: '../../../coverage/libs/model-ad/util', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/libs/model-ad/util/project.json b/libs/model-ad/util/project.json new file mode 100644 index 0000000000..bc776b4dc6 --- /dev/null +++ b/libs/model-ad/util/project.json @@ -0,0 +1,27 @@ +{ + "name": "model-ad-util", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "libs/model-ad/util/src", + "prefix": "model-ad", + "targets": { + "test": { + "executor": "@nx/jest:jest", + "outputs": [ + "{workspaceRoot}/coverage/libs/model-ad/util" + ], + "options": { + "jestConfig": "libs/model-ad/util/jest.config.ts" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + } + }, + "tags": [ + "type:util", + "scope:model-ad", + "language:typescript" + ], + "implicitDependencies": [] +} \ No newline at end of file diff --git a/libs/model-ad/util/src/index.ts b/libs/model-ad/util/src/index.ts new file mode 100644 index 0000000000..80502a42e6 --- /dev/null +++ b/libs/model-ad/util/src/index.ts @@ -0,0 +1 @@ +export * from './lib/default-seo-data'; diff --git a/libs/model-ad/util/src/lib/default-seo-data.ts b/libs/model-ad/util/src/lib/default-seo-data.ts new file mode 100644 index 0000000000..cc2af6ac5b --- /dev/null +++ b/libs/model-ad/util/src/lib/default-seo-data.ts @@ -0,0 +1,14 @@ +import { SeoData } from '@sagebionetworks/shared/util'; + +export const getDefaultSeoData = (): SeoData => { + return new SeoData({ + title: 'MODEL-AD', + description: + 'A complementary team of investigators from several laboratories and institutions', + url: '', + imageUrl: '', + imageAlt: 'MODEL-AD logo', + publishDate: '2023-09-20T00:00:00Z', + jsonLds: [], + }); +}; diff --git a/libs/model-ad/util/src/test-setup.ts b/libs/model-ad/util/src/test-setup.ts new file mode 100644 index 0000000000..1100b3e8a6 --- /dev/null +++ b/libs/model-ad/util/src/test-setup.ts @@ -0,0 +1 @@ +import 'jest-preset-angular/setup-jest'; diff --git a/libs/model-ad/util/tsconfig.json b/libs/model-ad/util/tsconfig.json new file mode 100644 index 0000000000..a092db18c2 --- /dev/null +++ b/libs/model-ad/util/tsconfig.json @@ -0,0 +1,26 @@ +{ + "extends": "../../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ], + "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "angularCompilerOptions": { + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/libs/model-ad/util/tsconfig.lib.json b/libs/model-ad/util/tsconfig.lib.json new file mode 100644 index 0000000000..f9941fa47e --- /dev/null +++ b/libs/model-ad/util/tsconfig.lib.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "declaration": true, + "declarationMap": true, + "inlineSources": true, + "types": [] + }, + "exclude": ["src/test-setup.ts", "**/*.spec.ts", "**/*.test.ts"], + "include": ["**/*.ts"] +} diff --git a/libs/model-ad/util/tsconfig.spec.json b/libs/model-ad/util/tsconfig.spec.json new file mode 100644 index 0000000000..81bff2d560 --- /dev/null +++ b/libs/model-ad/util/tsconfig.spec.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": ["**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] +} diff --git a/tsconfig.base.json b/tsconfig.base.json index 57697e0823..19af6ba5e2 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -10,53 +10,104 @@ "importHelpers": true, "target": "es2015", "module": "esnext", - "lib": ["es2017", "dom"], + "lib": [ + "es2017", + "dom" + ], "skipLibCheck": true, "skipDefaultLibCheck": true, "forceConsistentCasingInFileNames": true, "strict": true, "baseUrl": ".", "paths": { - "@sagebionetworks/openchallenges/about": ["libs/openchallenges/about/src/index.ts"], + "@sagebionetworks/openchallenges/about": [ + "libs/openchallenges/about/src/index.ts" + ], "@sagebionetworks/openchallenges/api-client-angular": [ "libs/openchallenges/api-client-angular/src/index.ts" ], - "@sagebionetworks/openchallenges/assets": ["libs/openchallenges/assets/src/index.ts"], - "@sagebionetworks/openchallenges/auth": ["libs/openchallenges/auth/src/index.ts"], - "@sagebionetworks/openchallenges/challenge": ["libs/openchallenges/challenge/src/index.ts"], + "@sagebionetworks/openchallenges/assets": [ + "libs/openchallenges/assets/src/index.ts" + ], + "@sagebionetworks/openchallenges/auth": [ + "libs/openchallenges/auth/src/index.ts" + ], + "@sagebionetworks/openchallenges/challenge": [ + "libs/openchallenges/challenge/src/index.ts" + ], "@sagebionetworks/openchallenges/challenge-search": [ "libs/openchallenges/challenge-search/src/index.ts" ], - "@sagebionetworks/openchallenges/config": ["libs/openchallenges/config/src/index.ts"], - "@sagebionetworks/openchallenges/home": ["libs/openchallenges/home/src/index.ts"], - "@sagebionetworks/openchallenges/login": ["libs/openchallenges/login/src/index.ts"], - "@sagebionetworks/openchallenges/not-found": ["libs/openchallenges/not-found/src/index.ts"], + "@sagebionetworks/openchallenges/config": [ + "libs/openchallenges/config/src/index.ts" + ], + "@sagebionetworks/openchallenges/home": [ + "libs/openchallenges/home/src/index.ts" + ], + "@sagebionetworks/openchallenges/login": [ + "libs/openchallenges/login/src/index.ts" + ], + "@sagebionetworks/openchallenges/not-found": [ + "libs/openchallenges/not-found/src/index.ts" + ], "@sagebionetworks/openchallenges/org-profile": [ "libs/openchallenges/org-profile/src/index.ts" ], - "@sagebionetworks/openchallenges/org-search": ["libs/openchallenges/org-search/src/index.ts"], - "@sagebionetworks/openchallenges/pages": ["libs/openchallenges/pages/src/index.ts"], - "@sagebionetworks/openchallenges/signup": ["libs/openchallenges/signup/src/index.ts"], - "@sagebionetworks/openchallenges/styles": ["libs/openchallenges/styles/src/index.ts"], - "@sagebionetworks/openchallenges/team": ["libs/openchallenges/team/src/index.ts"], - "@sagebionetworks/openchallenges/themes": ["libs/openchallenges/themes/src/index.ts"], - "@sagebionetworks/openchallenges/ui": ["libs/openchallenges/ui/src/index.ts"], + "@sagebionetworks/openchallenges/org-search": [ + "libs/openchallenges/org-search/src/index.ts" + ], + "@sagebionetworks/openchallenges/pages": [ + "libs/openchallenges/pages/src/index.ts" + ], + "@sagebionetworks/openchallenges/signup": [ + "libs/openchallenges/signup/src/index.ts" + ], + "@sagebionetworks/openchallenges/styles": [ + "libs/openchallenges/styles/src/index.ts" + ], + "@sagebionetworks/openchallenges/team": [ + "libs/openchallenges/team/src/index.ts" + ], + "@sagebionetworks/openchallenges/themes": [ + "libs/openchallenges/themes/src/index.ts" + ], + "@sagebionetworks/openchallenges/ui": [ + "libs/openchallenges/ui/src/index.ts" + ], "@sagebionetworks/openchallenges/user-profile": [ "libs/openchallenges/user-profile/src/index.ts" ], - "@sagebionetworks/openchallenges/util": ["libs/openchallenges/util/src/index.ts"], - "@sagebionetworks/shared/charts": ["libs/shared/typescript/charts/src/index.ts"], + "@sagebionetworks/openchallenges/util": [ + "libs/openchallenges/util/src/index.ts" + ], + "@sagebionetworks/shared/charts": [ + "libs/shared/typescript/charts/src/index.ts" + ], "@sagebionetworks/shared/charts-angular": [ "libs/shared/typescript/charts-angular/src/index.ts" ], - "@sagebionetworks/shared/util": ["libs/shared/typescript/util/src/index.ts"], + "@sagebionetworks/shared/util": [ + "libs/shared/typescript/util/src/index.ts" + ], "@sagebionetworks/shared/web-components": [ "libs/shared/typescript/web-components/src/index.ts" ], - "@sagebionetworks/model-ad/ui": ["libs/model-ad/ui/src/index.ts"], - "@sagebionetworks/model-ad/not-found": ["libs/model-ad/not-found/src/index.ts"], - "@sagebionetworks/model-ad/config": ["libs/model-ad/config/src/index.ts"] + "@sagebionetworks/model-ad/ui": [ + "libs/model-ad/ui/src/index.ts" + ], + "@sagebionetworks/model-ad/not-found": [ + "libs/model-ad/not-found/src/index.ts" + ], + "@sagebionetworks/model-ad/config": [ + "libs/model-ad/config/src/index.ts" + ], + "@sagebionetworks/model-ad/util": [ + "libs/model-ad/util/src/index.ts" + ], } }, - "exclude": ["node_modules", "tmp"] -} + "exclude": [ + "node_modules", + "tmp" + ] +} \ No newline at end of file