diff --git a/.github/workflows/deploy-client.yml b/.github/workflows/deploy-client.yml index 62382610..6527b905 100644 --- a/.github/workflows/deploy-client.yml +++ b/.github/workflows/deploy-client.yml @@ -40,6 +40,9 @@ jobs: run: | echo "git_head_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + - name: Init + run: pnpm run init --no-api --no-client + - name: Deploy to Vercel uses: amondnet/vercel-action@v25 id: vercel-action diff --git a/.github/workflows/main-client.yml b/.github/workflows/main-client.yml index 7545ff5c..6155c1b8 100644 --- a/.github/workflows/main-client.yml +++ b/.github/workflows/main-client.yml @@ -95,7 +95,7 @@ jobs: run: pnpm install - name: Init - run: pnpm run init --no-api --no-client + run: pnpm run init --no-api - name: Build run: pnpm run --filter ./client build diff --git a/api/gulpfile.ts b/api/gulpfile.ts index b7d8cea0..6028a494 100644 --- a/api/gulpfile.ts +++ b/api/gulpfile.ts @@ -2,7 +2,6 @@ import { exec as execNoPromise } from 'child_process'; import del from 'del'; import fs from 'fs'; import gulp, { type TaskFunction } from 'gulp'; -import ts from 'gulp-typescript'; import util from 'util'; import { generate as generatePrisma, pushDb, seedDb } from './prisma/utils/functions'; @@ -10,10 +9,8 @@ const exec = util.promisify(execNoPromise); // CONFIGS -const tsProject = ts.createProject('./tsconfig.json'); - export const configs = { - buildDest: (tsProject.config.compilerOptions?.outDir as string) || './dist', + buildDest: './dist', uploadsFolder: './uploads', devPort: 3005, prismaGeneratedFolder: 'src/_generated', @@ -43,6 +40,10 @@ function setupEnv() { return Promise.resolve(); } +function generateGraphQLSchema() { + return exec('pnpm exec nest start --entryFile="@common/graphql/schema/generate-schema"'); +} + function generatePrismaHelpers() { return generatePrisma(); } @@ -95,6 +96,8 @@ export const seed: TaskFunction = seedDatabase; export const cleanSeed: TaskFunction = gulp.series(cleanDb, seedDatabase); +export const setupGQLSchema: TaskFunction = gulp.series(setupPrisma, generateGraphQLSchema); + // Useful commands export { deleteDist, setupEnv }; diff --git a/api/package.json b/api/package.json index 77e27668..6ea324b7 100644 --- a/api/package.json +++ b/api/package.json @@ -18,6 +18,7 @@ "clean:db": "gulp cleanDb", "delete:dist": "gulp deleteDist", "generate": "gulp setupPrisma", + "gql:generate": "gulp setupGQLSchema", "lint": "npm-run-all -p lint:*", "lint:src": "eslint \"{src,apps,libs}/**/*.ts\"", "lint:tests": "eslint \"tests/**/*.ts\"", @@ -84,7 +85,6 @@ "graphql-config": "5.0.2", "graphql-tag": "2.12.6", "gulp": "4.0.2", - "gulp-typescript": "6.0.0-alpha.1", "prisma": "4.16.2", "prisma-fixtures": "0.1.15", "prisma-nestjs-graphql": "18.0.2", diff --git a/api/src/@common/graphql/schema/generate-schema.ts b/api/src/@common/graphql/schema/generate-schema.ts new file mode 100644 index 00000000..84f334b1 --- /dev/null +++ b/api/src/@common/graphql/schema/generate-schema.ts @@ -0,0 +1,5 @@ +import 'reflect-metadata'; + +import { ensureGraphQLSchema } from './schema.manager'; + +ensureGraphQLSchema(); diff --git a/api/src/@common/graphql/schema/schema.manager.ts b/api/src/@common/graphql/schema/schema.manager.ts new file mode 100644 index 00000000..a2225f73 --- /dev/null +++ b/api/src/@common/graphql/schema/schema.manager.ts @@ -0,0 +1,14 @@ +import { schemaPath } from '$graphql/graphql.module'; +import { NestFactory } from '@nestjs/core'; +import { existsSync } from 'fs'; +import { AppModule } from '~/app.module'; + +export async function ensureGraphQLSchema() { + if (!existsSync(schemaPath)) { + // If schema file doesn't exist, run app once to generate schema + const tempGqlApp = await NestFactory.create(AppModule); + + await tempGqlApp.init(); + await tempGqlApp.close(); + } +} diff --git a/api/src/@common/prisma/prisma.service.ts b/api/src/@common/prisma/prisma.service.ts index 53fdae70..330f1190 100644 --- a/api/src/@common/prisma/prisma.service.ts +++ b/api/src/@common/prisma/prisma.service.ts @@ -20,7 +20,13 @@ export class PrismaService extends PrismaClient implements OnModuleInit, OnModul } async onModuleInit(): Promise { - await this.$connect(); + await this.$connect().catch((error) => { + if (process.env.CI) { + return; + } + + throw error; + }); } async onModuleDestroy(): Promise { diff --git a/api/tests/utils/E2ETestManager.ts b/api/tests/utils/E2ETestManager.ts index 39f62c6d..371310a4 100644 --- a/api/tests/utils/E2ETestManager.ts +++ b/api/tests/utils/E2ETestManager.ts @@ -2,15 +2,14 @@ import 'lucia/polyfill/node'; import { AuthModule } from '$auth/auth.module'; import { AuthService } from '$auth/auth.service'; -import { GraphQLModule, schemaPath } from '$graphql/graphql.module'; +import { GraphQLModule } from '$graphql/graphql.module'; +import { ensureGraphQLSchema } from '$graphql/schema/schema.manager'; import { PrismaModule } from '$prisma/prisma.module'; import { PrismaService } from '$prisma/prisma.service'; import { setupViewEngine } from '$utils/setupViewEngine'; import { ValidationPipe } from '@nestjs/common'; -import { NestFactory } from '@nestjs/core'; import { NestExpressApplication } from '@nestjs/platform-express'; import { TestingModuleBuilder } from '@nestjs/testing'; -import { existsSync } from 'fs'; import { User } from 'lucia'; import supertest from 'supertest'; import supertestGQL, { Variables } from 'supertest-graphql'; @@ -177,13 +176,3 @@ export class E2ETestManager extends TestManager { this.authToken = session.sessionId; } } - -export async function ensureGraphQLSchema() { - if (!existsSync(schemaPath)) { - // If schema file doesn't exist, run app once to generate schema - const tempGqlApp = await NestFactory.create(AppModule); - - await tempGqlApp.init(); - await tempGqlApp.close(); - } -} diff --git a/api/tsconfig.build.json b/api/tsconfig.build.json index 2e7549eb..c845ecc9 100644 --- a/api/tsconfig.build.json +++ b/api/tsconfig.build.json @@ -7,8 +7,6 @@ "node_modules", "tests", "dist", - "**/*spec.ts", - ".graphqlrc.ts", - "gulpfile.ts", + "**/*.spec.ts", ] } \ No newline at end of file diff --git a/client/src/hooks.server.ts b/client/src/hooks.server.ts index 1f3664ab..00ad8886 100644 --- a/client/src/hooks.server.ts +++ b/client/src/hooks.server.ts @@ -5,7 +5,8 @@ import { parseString } from 'set-cookie-parser'; import type { AppLocals } from './app'; import { createHoudiniHelpers } from './lib/houdini/helper'; import { themeCookieName, themes, type Theme } from './lib/stores'; -import { AUTH_COOKIE_NAME } from './lib/utils/auth'; + +export const AUTH_COOKIE_NAME = 'auth_session'; export async function getAuthUser(query: AppLocals['gql']['query']) { const result = await query(GetUserFromSessionStore); diff --git a/client/src/lib/components/nav/Navbar.svelte b/client/src/lib/components/nav/Navbar.svelte index 9dd68a95..eac0a27b 100644 --- a/client/src/lib/components/nav/Navbar.svelte +++ b/client/src/lib/components/nav/Navbar.svelte @@ -2,9 +2,9 @@ import type { ClientUser } from '$/hooks.server'; import { navElements } from '$/navigation'; import { browser } from '$app/environment'; - import { applyAction, enhance } from '$app/forms'; + import { enhance } from '$app/forms'; import { page } from '$app/stores'; - import { isDrawerHidden, sessionToken } from '$lib/stores'; + import { isDrawerHidden } from '$lib/stores'; import { mdiLogout } from '@mdi/js'; import { Avatar, @@ -26,16 +26,6 @@ import { isNavElemVisible } from './utils'; export let sessionUser: ClientUser; - - const enhanceLogout: Parameters[1] = () => { - return async ({ result }) => { - if (result.type == 'success' || result.type == 'redirect') { - sessionToken.set(null); - } - - await applyAction(result); - }; - }; -
+ diff --git a/package.json b/package.json index ef28e3d6..d4079b98 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "./client" ], "scripts": { - "init": "ts-node-esm ./scripts/init.ts", + "init": "ts-node ./scripts/init.ts", "gql": "graphql-codegen --config .graphqlrc.ts", "gql:watch": "pnpm gql --watch" }, @@ -45,9 +45,6 @@ "stylelint": { "extends": [ "@v-ed/stylelint-config" - ], - "ignoreFiles": [ - "./client/static/theme/**/*" ] }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1d601fe8..12e378a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -223,9 +223,6 @@ importers: gulp: specifier: 4.0.2 version: 4.0.2 - gulp-typescript: - specifier: 6.0.0-alpha.1 - version: 6.0.0-alpha.1(typescript@5.1.6) prisma: specifier: 4.16.2 version: 4.16.2 @@ -252,7 +249,7 @@ importers: version: 1.3.2(@swc/core@1.3.68)(webpack@5.88.1) vite-tsconfig-paths: specifier: 4.2.0 - version: 4.2.0(typescript@5.1.6)(vite@4.4.9) + version: 4.2.0(typescript@5.1.6) vitest: specifier: 0.32.4 version: 0.32.4 @@ -4651,6 +4648,7 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + requiresBuild: true /aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} @@ -5221,6 +5219,7 @@ packages: /callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + requiresBuild: true /camel-case@4.1.2: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} @@ -7698,21 +7697,6 @@ packages: - supports-color dev: true - /gulp-typescript@6.0.0-alpha.1(typescript@5.1.6): - resolution: {integrity: sha512-KoT0TTfjfT7w3JItHkgFH1T/zK4oXWC+a8xxKfniRfVcA0Fa1bKrIhztYelYmb+95RB80OLMBreknYkdwzdi2Q==} - engines: {node: '>= 8'} - peerDependencies: - typescript: '~2.7.1 || >=2.8.0-dev || >=2.9.0-dev || ~3.0.0 || >=3.0.0-dev || >=3.1.0-dev || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.7.0-dev ' - dependencies: - ansi-colors: 4.1.3 - plugin-error: 1.0.1 - source-map: 0.7.4 - through2: 3.0.2 - typescript: 5.1.6 - vinyl: 2.2.1 - vinyl-fs: 3.0.3 - dev: true - /gulp@4.0.2: resolution: {integrity: sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==} engines: {node: '>= 0.10'} @@ -8202,6 +8186,7 @@ packages: /is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + requiresBuild: true /is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} @@ -8619,6 +8604,7 @@ packages: /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + requiresBuild: true dependencies: argparse: 2.0.1 @@ -8638,6 +8624,7 @@ packages: /json-parse-even-better-errors@3.0.0: resolution: {integrity: sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + requiresBuild: true dev: false optional: true @@ -8798,10 +8785,12 @@ packages: /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + requiresBuild: true /lines-and-columns@2.0.3: resolution: {integrity: sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + requiresBuild: true dev: false optional: true @@ -9928,6 +9917,7 @@ packages: /parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + requiresBuild: true dependencies: callsites: 3.1.0 @@ -10205,16 +10195,6 @@ packages: hasBin: true dev: true - /plugin-error@1.0.1: - resolution: {integrity: sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==} - engines: {node: '>= 0.10'} - dependencies: - ansi-colors: 1.1.0 - arr-diff: 4.0.0 - arr-union: 3.1.0 - extend-shallow: 3.0.2 - dev: true - /pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -10886,6 +10866,7 @@ packages: /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} + requiresBuild: true /resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} @@ -12152,6 +12133,7 @@ packages: dependencies: inherits: 2.0.4 readable-stream: 3.6.2 + dev: false /through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} @@ -12469,6 +12451,7 @@ packages: /type-fest@3.12.0: resolution: {integrity: sha512-qj9wWsnFvVEMUDbESiilKeXeHL7FwwiFcogfhfyjmvT968RXSvnl23f1JOClTHYItsi7o501C/7qVllscUP3oA==} engines: {node: '>=14.16'} + requiresBuild: true dev: false optional: true @@ -12889,6 +12872,22 @@ packages: micromatch: 4.0.5 dev: true + /vite-tsconfig-paths@4.2.0(typescript@5.1.6): + resolution: {integrity: sha512-jGpus0eUy5qbbMVGiTxCL1iB9ZGN6Bd37VGLJU39kTDD6ZfULTTb1bcc5IeTWqWJKiWV5YihCaibeASPiGi8kw==} + peerDependencies: + vite: '*' + peerDependenciesMeta: + vite: + optional: true + dependencies: + debug: 4.3.4 + globrex: 0.1.2 + tsconfck: 2.1.1(typescript@5.1.6) + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /vite-tsconfig-paths@4.2.0(typescript@5.1.6)(vite@4.4.9): resolution: {integrity: sha512-jGpus0eUy5qbbMVGiTxCL1iB9ZGN6Bd37VGLJU39kTDD6ZfULTTb1bcc5IeTWqWJKiWV5YihCaibeASPiGi8kw==} peerDependencies: diff --git a/scripts/init.ts b/scripts/init.ts index 2cdb0a3c..020462fd 100644 --- a/scripts/init.ts +++ b/scripts/init.ts @@ -3,33 +3,30 @@ import util from 'util'; import { setupRenovate } from './subscripts/file-changes.js'; import { setupProjectName } from './subscripts/project-name.js'; import { setupEnvs } from './subscripts/setup-envs.js'; -import { getCliArgs } from './utils/cli.js'; +import { getCliArgs } from './utils/args.js'; +import { progresser } from './utils/index.js'; const exec = util.promisify(execNoPromise); +const { args } = getCliArgs(); + // ===================== // Setup Generic Files // ===================== -process.stdout.write('Setting up renovate...'); -await setupRenovate(); -console.log(' Done!'); +await progresser('Setting up renovate', setupRenovate); // ===================== -// Setup Envs +// Setup Envs // ===================== -process.stdout.write('Setting up env vars...'); -await setupEnvs(); -console.log(' Done!'); +await progresser('Setting up env vars', setupEnvs); // ===================== // Init API // ===================== -const { args } = getCliArgs(); - -if (!args.some((arg) => arg == '--no-api')) { +if (!args.some(({ name }) => name == 'no-api')) { try { const { stdout } = await exec( `if [ $(pgrep -f 'pnpm start:debug$' -d ' ') ] ; then echo Killing API process to allow for init to run properly. Remember to start it again / reload at the end! ; fi`, @@ -43,18 +40,19 @@ if (!args.some((arg) => arg == '--no-api')) { } catch (error) { // Do nothing on api kill error } - process.stdout.write('Setting up api...'); - await exec('pnpm run --filter ./api init'); - - console.log(' Done!'); + await progresser('Setting up api', () => exec('pnpm run --filter ./api init')); +} else if (!args.some(({ name }) => name == 'no-gql')) { + // If not setting API, at least check for GraphQL generation + await progresser('Setting up GraphQL', () => exec('pnpm run --filter ./api gql:generate')); } -if (!args.some((arg) => arg == '--no-client')) { - process.stdout.write('Setting up client...'); - await exec('pnpm run --filter ./client sync'); +// ===================== +// Init Client +// ===================== - console.log(' Done!'); +if (!args.some(({ name }) => name == 'no-client')) { + await progresser('Setting up client', () => exec('pnpm run --filter ./client sync')); } // No need to setup project name in CI diff --git a/scripts/subscripts/project-name.ts b/scripts/subscripts/project-name.ts index a8ea39c9..69614d41 100644 --- a/scripts/subscripts/project-name.ts +++ b/scripts/subscripts/project-name.ts @@ -24,9 +24,7 @@ async function getOldProjectName(rl: Interface) { return; } - const files = fs.readdirSync('.').filter((fn) => fn.endsWith('.code-workspace')); - - const workspaceFile = files[0]; + const workspaceFile = fs.readdirSync('.').find((fn) => fn.endsWith('.code-workspace')); if (!workspaceFile) { throw 'Logic to find workspace file name failed, check your file permissions!'; diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json index 929132e5..1f16d2dd 100644 --- a/scripts/tsconfig.json +++ b/scripts/tsconfig.json @@ -6,4 +6,12 @@ "target": "es2022", }, "include": ["./**/*.ts"], + "ts-node": { + "esm": true, + "experimentalSpecifierResolution": "node", + "moduleTypes": { + "**/*.ts": "esm" + }, + "require": ["tsconfig-paths/register"], + }, } diff --git a/scripts/utils/args.ts b/scripts/utils/args.ts new file mode 100644 index 00000000..fb550c83 --- /dev/null +++ b/scripts/utils/args.ts @@ -0,0 +1,52 @@ +/* eslint-disable @typescript-eslint/no-magic-numbers */ + +export type Arg = { + name: string; + value?: string; + type: 'arg' | 'value'; + pos: number; +}; + +export function formatRawArgs(rawArgs: string[]): Arg[] { + function splitNameAndValue(arg: string) { + if (arg.includes('=')) { + const [name, value] = arg.split('=', 2) as [string, string]; + + return { name, value }; + } + + return { name: arg }; + } + + const unFlattenArgs = rawArgs.map((arg, pos) => { + if (arg.startsWith('--')) { + const formattedArg = arg.slice(2); + + return { ...splitNameAndValue(formattedArg), pos, type: 'arg' }; + } + + if (arg.startsWith('-')) { + return arg + .slice(1) + .split('') + .map((singleArg) => ({ name: singleArg, pos, type: 'arg' })); + } + + return { ...splitNameAndValue(arg), pos, type: 'value' }; + }); + + return unFlattenArgs.flat(); +} + +export function getCliArgs() { + const [executable, file, ...rawArgs] = process.argv; + + const args = formatRawArgs(rawArgs); + + return { + executable, + file, + args, + rawArgs, + }; +} diff --git a/scripts/utils/cli.ts b/scripts/utils/cli.ts deleted file mode 100644 index 7548028d..00000000 --- a/scripts/utils/cli.ts +++ /dev/null @@ -1,9 +0,0 @@ -export function getCliArgs() { - const [executable, file, ...args] = process.argv; - - return { - executable, - file, - args, - }; -} diff --git a/scripts/utils/index.ts b/scripts/utils/index.ts index f520bd6e..aa30160b 100644 --- a/scripts/utils/index.ts +++ b/scripts/utils/index.ts @@ -18,3 +18,21 @@ export function generateGuid() { }) .join(''); } + +export async function progresser(text: string, func: () => unknown | Promise) { + const perfStart = `${text}-start`; + const perfEnd = `${text}-end`; + + performance.mark(perfStart); + + process.stdout.write(`${text}...`); + + await func(); + + performance.mark(perfEnd); + + const perf = performance.measure(text, perfStart, perfEnd); + const duration = Math.floor(perf.duration); + + console.log(` Done! (${duration} ms)`); +}