mirror of https://github.com/sylv/micro.git
chore: eslint with strict type imports
This commit is contained in:
parent
f849e80984
commit
ee0c6ffa4a
|
@ -4,7 +4,7 @@
|
|||
"npm.scriptExplorerExclude": ["^((?!watch|generate:watch).)*$"],
|
||||
"eslint.workingDirectories": [
|
||||
{
|
||||
"pattern": "./{packages,apps}/*"
|
||||
"pattern": "./packages/*"
|
||||
}
|
||||
],
|
||||
"files.associations": {
|
||||
|
|
|
@ -5,6 +5,7 @@ RUN npm i -g pnpm
|
|||
|
||||
WORKDIR /usr/src/micro
|
||||
|
||||
COPY patches patches
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
|
||||
pnpm fetch
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"syncpack": "^12.3.0",
|
||||
"turbo": "1.11.3",
|
||||
"turbo": "1.12.3",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"packageManager": "pnpm@7.0.0"
|
||||
|
|
|
@ -5,5 +5,7 @@ module.exports = {
|
|||
},
|
||||
rules: {
|
||||
'unicorn/no-abusive-eslint-disable': 'off',
|
||||
'unicorn/filename-case': 'off',
|
||||
'import/no-default-export': 'off',
|
||||
},
|
||||
};
|
|
@ -26,24 +26,24 @@
|
|||
"@mikro-orm/migrations": "^5.9.7",
|
||||
"@mikro-orm/nestjs": "^5.2.3",
|
||||
"@mikro-orm/postgresql": "^5.9.7",
|
||||
"@nestjs/common": "^10.3.0",
|
||||
"@nestjs/core": "^10.3.0",
|
||||
"@nestjs/graphql": "^12.0.11",
|
||||
"@nestjs/common": "^10.3.2",
|
||||
"@nestjs/core": "^10.3.2",
|
||||
"@nestjs/graphql": "^12.1.1",
|
||||
"@nestjs/jwt": "^10.2.0",
|
||||
"@nestjs/mercurius": "^12.0.11",
|
||||
"@nestjs/mercurius": "^12.1.1",
|
||||
"@nestjs/passport": "^10.0.3",
|
||||
"@nestjs/platform-fastify": "^10.3.0",
|
||||
"@nestjs/schedule": "^4.0.0",
|
||||
"@nestjs/platform-fastify": "^10.3.2",
|
||||
"@nestjs/schedule": "^4.0.1",
|
||||
"@ryanke/venera": "^1.0.5",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.0",
|
||||
"fastify": "^4.25.2",
|
||||
"fastify": "^4.26.0",
|
||||
"fluent-ffmpeg": "^2.1.2",
|
||||
"graphql": "^16.8.1",
|
||||
"mercurius": "^13.3.3",
|
||||
"mime-types": "^2.1.35",
|
||||
"nodemailer": "^6.9.8",
|
||||
"nodemailer": "^6.9.9",
|
||||
"otplib": "^12.0.1",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
|
@ -55,7 +55,7 @@
|
|||
"devDependencies": {
|
||||
"@atlasbot/configs": "^10.5.15",
|
||||
"@mikro-orm/cli": "^5.9.7",
|
||||
"@swc/core": "^1.3.102",
|
||||
"@swc/core": "^1.4.0",
|
||||
"@types/bcryptjs": "^2.4.6",
|
||||
"@types/bytes": "^3.1.4",
|
||||
"@types/dedent": "^0.7.2",
|
||||
|
@ -63,7 +63,7 @@
|
|||
"@types/luxon": "^3.4.0",
|
||||
"@types/mime-types": "^2.1.4",
|
||||
"@types/ms": "^0.7.34",
|
||||
"@types/node": "^20.10.6",
|
||||
"@types/node": "^20.11.17",
|
||||
"@types/nodemailer": "^6.4.14",
|
||||
"@types/passport-jwt": "^4.0.0",
|
||||
"@types/utf-8-validate": "^5.0.2",
|
||||
|
@ -72,21 +72,21 @@
|
|||
"content-range": "^2.0.2",
|
||||
"dedent": "^1.5.1",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"file-type": "^18.7.0",
|
||||
"file-type": "^19.0.0",
|
||||
"handlebars": "^4.7.8",
|
||||
"istextorbinary": "^9.5.0",
|
||||
"luxon": "^3.4.4",
|
||||
"ms": "^3.0.0-canary.1",
|
||||
"nanoid": "^5.0.4",
|
||||
"nanoid": "^5.0.5",
|
||||
"normalize-url": "^8.0.0",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
"reflect-metadata": "^0.2.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsup": "^8.0.1",
|
||||
"tsup": "^8.0.2",
|
||||
"typescript": "^5.3.3",
|
||||
"vitest": "^1.1.3",
|
||||
"vitest": "^1.2.2",
|
||||
"zod": "^3.22.4",
|
||||
"zod-validation-error": "^2.1.0"
|
||||
"zod-validation-error": "^3.0.0"
|
||||
},
|
||||
"mikro-orm": {
|
||||
"useTsNode": true,
|
||||
|
|
|
@ -11,8 +11,8 @@ import { Transform } from 'stream';
|
|||
export class ExifTransformer extends Transform {
|
||||
private static readonly app1Marker = Buffer.from('ffe1', 'hex');
|
||||
private static readonly exifMarker = Buffer.from('457869660000', 'hex'); // Exif\0\0
|
||||
private static readonly xmpMarker = Buffer.from('http://ns.adobe.com/xap', 'utf-8');
|
||||
private static readonly flirMarker = Buffer.from('FLIR', 'utf-8');
|
||||
private static readonly xmpMarker = Buffer.from('http://ns.adobe.com/xap', 'utf8');
|
||||
private static readonly flirMarker = Buffer.from('FLIR', 'utf8');
|
||||
private static readonly maxMarkerLength = Math.max(
|
||||
ExifTransformer.exifMarker.length,
|
||||
ExifTransformer.xmpMarker.length,
|
||||
|
@ -48,7 +48,7 @@ export class ExifTransformer extends Transform {
|
|||
// no app1 in the current pendingChunk
|
||||
if (app1Start === -1) {
|
||||
// if last byte is ff, wait for more
|
||||
if (!atEnd && pendingChunk[pendingChunk.length - 1] === ExifTransformer.app1Marker[0]) {
|
||||
if (!atEnd && pendingChunk.at(-1) === ExifTransformer.app1Marker[0]) {
|
||||
if (chunk) this.pending.push(chunk);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ const enhanceHost = (host: z.infer<typeof schema>['hosts'][0]) => {
|
|||
};
|
||||
|
||||
export const config = result.data as Omit<z.infer<typeof schema>, 'hosts'>;
|
||||
export const hosts = result.data.hosts.map(enhanceHost);
|
||||
export const hosts = result.data.hosts.map((host) => enhanceHost(host));
|
||||
export const rootHost = hosts[0];
|
||||
|
||||
if (rootHost.isWildcard) {
|
||||
|
@ -92,9 +92,9 @@ if (disallowed.has(config.secret.toLowerCase())) {
|
|||
const token = randomBytes(24).toString('hex');
|
||||
throw new Error(
|
||||
dedent`
|
||||
${c.redBright.bold('Do not use the default secret.')}
|
||||
Please generate a random, secure secret or you risk anyone being able to impersonate you.
|
||||
If you're lazy, here is a random secret: ${c.underline(token)}
|
||||
${c.redBright.bold('Do not use the default secret.')}
|
||||
Please generate a random, secure secret or you risk anyone being able to impersonate you.
|
||||
If you're lazy, here is a random secret: ${c.underline(token)}
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ export const expandMime = (input: string | string[]) => {
|
|||
if (!Array.isArray(input)) input = [input];
|
||||
const output: string[] = [];
|
||||
for (const mimeType of input) {
|
||||
const alias = MIME_MAP.get(mimeType.replace(WILDCARD_REGEX, ''));
|
||||
const alias = MIME_MAP.get(mimeType.replaceAll(WILDCARD_REGEX, ''));
|
||||
if (alias) {
|
||||
output.push(...alias);
|
||||
continue;
|
||||
|
|
|
@ -18,8 +18,7 @@ export function parseCursor(cursor: string) {
|
|||
|
||||
export function paginate<T>(items: T[], total: number, offset: number): Paginated<T> {
|
||||
const edges: Edge<T>[] = [];
|
||||
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
|
||||
const item = items[itemIndex];
|
||||
for (const [itemIndex, item] of items.entries()) {
|
||||
const cursor = createCursor(offset + itemIndex);
|
||||
edges.push({
|
||||
cursor: cursor,
|
||||
|
@ -31,7 +30,7 @@ export function paginate<T>(items: T[], total: number, offset: number): Paginate
|
|||
edges: edges,
|
||||
totalCount: total,
|
||||
pageInfo: {
|
||||
endCursor: edges[0] ? edges[edges.length - 1].cursor : undefined,
|
||||
endCursor: edges[0] ? edges.at(-1)!.cursor : undefined,
|
||||
startCursor: edges[0] ? edges[0].cursor : undefined,
|
||||
hasPreviousPage: offset > 0,
|
||||
hasNextPage: offset + items.length < total,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import type { IdentifiedReference } from '@mikro-orm/core';
|
||||
import type { Ref } from '@mikro-orm/core';
|
||||
import { BeforeCreate, Entity, Property, type EventArgs } from '@mikro-orm/core';
|
||||
import { ObjectType } from '@nestjs/graphql';
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
|
@ -14,7 +13,7 @@ export abstract class Resource {
|
|||
@Property({ nullable: true })
|
||||
hostname?: string;
|
||||
|
||||
abstract owner?: IdentifiedReference<User>;
|
||||
abstract owner?: Ref<User>;
|
||||
abstract getPaths(): ResourceLocations;
|
||||
|
||||
getUrls() {
|
||||
|
|
|
@ -11,64 +11,56 @@ import { migrate } from './migrate.js';
|
|||
import { AppModule } from './modules/app.module.js';
|
||||
import { HostGuard } from './modules/host/host.guard.js';
|
||||
|
||||
async function bootstrap() {
|
||||
await migrate();
|
||||
await migrate();
|
||||
|
||||
const logger = new Logger('bootstrap');
|
||||
const server = fastify({
|
||||
trustProxy: process.env.TRUST_PROXY === 'true',
|
||||
maxParamLength: 500,
|
||||
bodyLimit: config.uploadLimit,
|
||||
});
|
||||
|
||||
const adapter = new FastifyAdapter(server as any);
|
||||
const app = await NestFactory.create<NestFastifyApplication>(AppModule, adapter);
|
||||
app.useGlobalGuards(new HostGuard());
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
whitelist: true,
|
||||
forbidNonWhitelisted: true,
|
||||
forbidUnknownValues: true,
|
||||
exceptionFactory(errors) {
|
||||
// without this, nestjs won't include validation errors in the graphql response,
|
||||
// just a blank bad request error, which is just a little confusing. thanks nestjs!
|
||||
const formattedErrors = errors.map((error) => {
|
||||
if (error.constraints) {
|
||||
const constraints = Object.values(error.constraints);
|
||||
if (constraints[0]) return constraints.join(', ');
|
||||
}
|
||||
|
||||
return error.toString();
|
||||
});
|
||||
|
||||
return new BadRequestException(formattedErrors.join('\n'));
|
||||
},
|
||||
transformOptions: {
|
||||
enableImplicitConversion: true,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
await app.register(fastifyCookie as any);
|
||||
await app.register(fastifyHelmet.default as any);
|
||||
await app.register(fastifyMultipart.default as any, {
|
||||
limits: {
|
||||
fieldNameSize: 100,
|
||||
fieldSize: 100,
|
||||
fields: 0,
|
||||
files: 1,
|
||||
headerPairs: 20,
|
||||
},
|
||||
});
|
||||
|
||||
await app.listen(8080, '0.0.0.0', (error, address) => {
|
||||
if (error) throw error;
|
||||
logger.log(`Listening at ${address}`);
|
||||
});
|
||||
}
|
||||
|
||||
// top-level await is not supported by ncc
|
||||
bootstrap().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
const logger = new Logger('bootstrap');
|
||||
const server = fastify({
|
||||
trustProxy: process.env.TRUST_PROXY === 'true',
|
||||
maxParamLength: 500,
|
||||
bodyLimit: config.uploadLimit,
|
||||
});
|
||||
|
||||
const adapter = new FastifyAdapter(server as any);
|
||||
const app = await NestFactory.create<NestFastifyApplication>(AppModule, adapter);
|
||||
app.useGlobalGuards(new HostGuard());
|
||||
app.useGlobalPipes(
|
||||
new ValidationPipe({
|
||||
whitelist: true,
|
||||
forbidNonWhitelisted: true,
|
||||
forbidUnknownValues: true,
|
||||
exceptionFactory(errors) {
|
||||
// without this, nestjs won't include validation errors in the graphql response,
|
||||
// just a blank bad request error, which is just a little confusing. thanks nestjs!
|
||||
const formattedErrors = errors.map((error) => {
|
||||
if (error.constraints) {
|
||||
const constraints = Object.values(error.constraints);
|
||||
if (constraints[0]) return constraints.join(', ');
|
||||
}
|
||||
|
||||
return error.toString();
|
||||
});
|
||||
|
||||
return new BadRequestException(formattedErrors.join('\n'));
|
||||
},
|
||||
transformOptions: {
|
||||
enableImplicitConversion: true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await app.register(fastifyCookie as any);
|
||||
await app.register(fastifyHelmet.default as any);
|
||||
await app.register(fastifyMultipart.default as any, {
|
||||
limits: {
|
||||
fieldNameSize: 100,
|
||||
fieldSize: 100,
|
||||
fields: 0,
|
||||
files: 1,
|
||||
headerPairs: 20,
|
||||
},
|
||||
});
|
||||
|
||||
await app.listen(8080, '0.0.0.0', (error, address) => {
|
||||
if (error) throw error;
|
||||
logger.log(`Listening at ${address}`);
|
||||
});
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Controller, Get, Req, UseGuards } from '@nestjs/common';
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
import { config, hosts, rootHost } from '../config.js';
|
||||
import { UserId } from './auth/auth.decorators.js';
|
||||
import { OptionalJWTAuthGuard } from './auth/guards/optional-jwt.guard.js';
|
||||
import { UserService } from './user/user.service.js';
|
||||
import { type FastifyRequest } from 'fastify';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
|
@ -23,7 +23,7 @@ export class AppController {
|
|||
return {
|
||||
inquiries: config.inquiries,
|
||||
uploadLimit: config.uploadLimit,
|
||||
allowTypes: config.allowTypes ? [...config.allowTypes?.values()] : undefined,
|
||||
allowTypes: config.allowTypes ? [...config.allowTypes.values()] : undefined,
|
||||
email: !!config.email,
|
||||
rootHost: {
|
||||
url: rootHost.url,
|
||||
|
|
|
@ -25,7 +25,7 @@ export class AppResolver {
|
|||
return {
|
||||
inquiriesEmail: config.inquiries,
|
||||
uploadLimit: config.uploadLimit,
|
||||
allowTypes: config.allowTypes ? [...config.allowTypes?.values()] : [],
|
||||
allowTypes: config.allowTypes ? [...config.allowTypes.values()] : [],
|
||||
requireEmails: !!config.email,
|
||||
rootHost: this.filterHost(rootHost),
|
||||
currentHost: this.filterHost(currentHost),
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
Res,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { type FastifyReply, type FastifyRequest } from 'fastify';
|
||||
import { rootHost } from '../../config.js';
|
||||
import { UserId } from '../auth/auth.decorators.js';
|
||||
import { JWTAuthGuard } from '../auth/guards/jwt.guard.js';
|
||||
|
|
|
@ -72,8 +72,13 @@ export class File extends Resource {
|
|||
}
|
||||
|
||||
getDisplayName() {
|
||||
if (this.name) return this.name;
|
||||
const extension = this.getExtension();
|
||||
return this.name ? this.name : extension ? `${this.id}.${extension}` : this.id;
|
||||
if (extension) {
|
||||
return `${this.id}.${extension}`;
|
||||
}
|
||||
|
||||
return this.id;
|
||||
}
|
||||
|
||||
getPaths() {
|
||||
|
|
|
@ -118,8 +118,9 @@ export class FileService implements OnApplicationBootstrap {
|
|||
uploadStream = uploadStream.pipe(transformer).pipe(new PassThrough());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
default: {
|
||||
throw new Error(`Unknown or unsupported conversion ${fromGroup} to ${toGroup}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { InjectRepository } from '@mikro-orm/nestjs';
|
||||
import { EntityRepository } from '@mikro-orm/postgresql';
|
||||
import { Controller, Get, Param, Request, Res } from '@nestjs/common';
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { type FastifyReply, type FastifyRequest } from 'fastify';
|
||||
import { Link } from './link.entity.js';
|
||||
import { LinkService } from './link.service.js';
|
||||
|
||||
|
@ -9,7 +9,7 @@ import { LinkService } from './link.service.js';
|
|||
export class LinkController {
|
||||
constructor(
|
||||
@InjectRepository(Link) private readonly linkRepo: EntityRepository<Link>,
|
||||
private readonly linkService: LinkService
|
||||
private readonly linkService: LinkService,
|
||||
) {}
|
||||
|
||||
@Get('link/:id')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Controller, Get, Param, Req } from '@nestjs/common';
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
import { type FastifyRequest } from 'fastify';
|
||||
import { PasteService } from './paste.service.js';
|
||||
|
||||
@Controller('paste')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Controller, Get, Param, Req, Res } from '@nestjs/common';
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { type FastifyReply, type FastifyRequest } from 'fastify';
|
||||
import { ThumbnailService } from './thumbnail.service.js';
|
||||
|
||||
@Controller()
|
||||
|
@ -10,7 +10,7 @@ export class ThumbnailController {
|
|||
async getThumbnailContent(
|
||||
@Param('fileId') fileId: string,
|
||||
@Req() request: FastifyRequest,
|
||||
@Res() reply: FastifyReply
|
||||
@Res() reply: FastifyReply,
|
||||
) {
|
||||
return this.thumbnailService.sendThumbnail(fileId, request, reply);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable no-await-in-loop */
|
||||
import { EntityRepository } from '@mikro-orm/core';
|
||||
import { InjectRepository } from '@mikro-orm/nestjs';
|
||||
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
|
||||
|
@ -23,7 +24,7 @@ export class ThumbnailService {
|
|||
private static readonly IMAGE_TYPES = new Set(
|
||||
Object.keys(sharp.format)
|
||||
.map((key) => mime.lookup(key))
|
||||
.filter((key) => key && key.startsWith('image'))
|
||||
.filter((key) => key && key.startsWith('image')),
|
||||
);
|
||||
|
||||
private static readonly VIDEO_TYPES = new Set([
|
||||
|
@ -42,7 +43,7 @@ export class ThumbnailService {
|
|||
@InjectRepository('Thumbnail') private readonly thumbnailRepo: EntityRepository<Thumbnail>,
|
||||
@InjectRepository('File') private readonly fileRepo: EntityRepository<File>,
|
||||
private readonly storageService: StorageService,
|
||||
private readonly fileService: FileService
|
||||
private readonly fileService: FileService,
|
||||
) {}
|
||||
|
||||
async getThumbnail(fileId: string) {
|
||||
|
@ -111,8 +112,7 @@ export class ThumbnailService {
|
|||
// and it is so whatever. maybe there is a way to do this faster, but this is already pretty fast.
|
||||
const positions = ['5%', '10%', '20%', '40%'];
|
||||
const size = `${ThumbnailService.THUMBNAIL_SIZE}x?`;
|
||||
for (let positionIndex = 0; positionIndex < positions.length; positionIndex++) {
|
||||
const percent = positions[positionIndex];
|
||||
for (const [positionIndex, percent] of positions.entries()) {
|
||||
const stream = ffmpeg(filePath).screenshot({
|
||||
count: 1,
|
||||
timemarks: [percent],
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Controller, Get, Param, Response } from '@nestjs/common';
|
||||
import type { FastifyReply } from 'fastify';
|
||||
import { type FastifyReply } from 'fastify';
|
||||
import { UserService } from './user.service.js';
|
||||
|
||||
@Controller()
|
||||
|
@ -10,7 +10,7 @@ export class UserController {
|
|||
async verifyUser(
|
||||
@Param('userId') userId: string,
|
||||
@Param('verifyId') verifyId: string,
|
||||
@Response() reply: FastifyReply
|
||||
@Response() reply: FastifyReply,
|
||||
) {
|
||||
await this.userService.verifyUser(userId, verifyId);
|
||||
return reply.redirect(302, '/login?verified=true');
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
||||
/* eslint-disable import/no-default-export */
|
||||
import { FlushMode } from '@mikro-orm/core';
|
||||
import type { MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs';
|
||||
import { Logger, NotFoundException } from '@nestjs/common';
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable unicorn/no-keyword-prefix */
|
||||
import type { Type } from '@nestjs/common';
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable unicorn/no-keyword-prefix */
|
||||
import type { Type } from '@nestjs/common';
|
||||
import { Field, Int, ObjectType } from '@nestjs/graphql';
|
||||
import { Edge } from './edge.type.js';
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"noUncheckedIndexedAccess": false,
|
||||
"strictPropertyInitialization": false,
|
||||
"strictPropertyInitialization": false, // doesn't play nice with mikroorm/graphql decorators
|
||||
"noImplicitOverride": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"lib": ["es2021", "dom"]
|
||||
}
|
||||
"lib": ["es2021", "dom"],
|
||||
},
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
module.exports = {
|
||||
extends: require.resolve('@atlasbot/configs/eslint/react'),
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-floating-promises': 'off',
|
||||
'jsx-a11y/no-autofocus': 'off',
|
||||
'jsx-a11y/media-has-caption': 'off',
|
||||
'unicorn/consistent-destructuring': 'off',
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'unicorn/filename-case': 'off',
|
||||
'import/no-default-export': 'off',
|
||||
'@typescript-eslint/consistent-type-imports': [
|
||||
'error',
|
||||
{
|
||||
prefer: 'type-imports',
|
||||
disallowTypeAnnotations: false,
|
||||
fixStyle: 'separate-type-imports',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
|
@ -1,11 +0,0 @@
|
|||
module.exports = {
|
||||
extends: require.resolve('@atlasbot/configs/eslint/next'),
|
||||
parserOptions: {
|
||||
project: './tsconfig.json',
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-floating-promises': 'off',
|
||||
'jsx-a11y/no-autofocus': 'off',
|
||||
'jsx-a11y/media-has-caption': 'off',
|
||||
},
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
import { CodegenConfig } from '@graphql-codegen/cli';
|
||||
import type { CodegenConfig } from '@graphql-codegen/cli';
|
||||
|
||||
export default {
|
||||
overwrite: true,
|
||||
|
@ -17,9 +17,6 @@ export default {
|
|||
fragmentMasking: false,
|
||||
},
|
||||
},
|
||||
'src/@generated/introspection.json': {
|
||||
plugins: ['introspection'],
|
||||
},
|
||||
},
|
||||
hooks: {
|
||||
afterAllFileWrite: ['prettier --write'],
|
||||
|
|
|
@ -11,17 +11,14 @@
|
|||
},
|
||||
"scripts": {
|
||||
"build": "tsc --noEmit && rm -rf ./dist/* && vavite build && tsup && rm -rf ./dist/server",
|
||||
"generate": "graphql-codegen --config codegen.ts",
|
||||
"start": "node ./dist/index.js",
|
||||
"watch": "concurrently \"vavite serve\" \"pnpm generate --watch\""
|
||||
"watch": "vavite serve"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@atlasbot/configs": "^10.5.15",
|
||||
"@fastify/early-hints": "^1.0.1",
|
||||
"@fastify/http-proxy": "^9.3.0",
|
||||
"@graphql-codegen/cli": "^5.0.0",
|
||||
"@graphql-codegen/client-preset": "^4.1.0",
|
||||
"@graphql-codegen/introspection": "^4.0.0",
|
||||
"@fastify/http-proxy": "^9.4.0",
|
||||
"@graphql-codegen/client-preset": "^4.2.2",
|
||||
"@graphql-typed-document-node/core": "^3.2.0",
|
||||
"@parcel/watcher": "^2.3.0",
|
||||
"@preact/preset-vite": "^2.8.1",
|
||||
|
@ -32,20 +29,19 @@
|
|||
"@urql/preact": "^4.0.4",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"clsx": "^2.1.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"dayjs": "^1.11.10",
|
||||
"fastify": "^4.25.2",
|
||||
"fastify": "^4.26.0",
|
||||
"formik": "^2.4.5",
|
||||
"generate-avatar": "1.4.10",
|
||||
"graphql": "^16.8.1",
|
||||
"http-status-codes": "^2.3.0",
|
||||
"nanoid": "^5.0.4",
|
||||
"nanoid": "^5.0.5",
|
||||
"path-to-regexp": "^6.2.1",
|
||||
"postcss": "^8.4.33",
|
||||
"preact": "^10.19.3",
|
||||
"postcss": "^8.4.35",
|
||||
"preact": "^10.19.4",
|
||||
"preact-render-to-string": "^6.3.1",
|
||||
"prettier": "^3.1.1",
|
||||
"prettier": "^3.2.5",
|
||||
"prism-react-renderer": "^2.3.1",
|
||||
"qrcode.react": "^3.1.0",
|
||||
"react": "npm:@preact/compat@^17.1.2",
|
||||
|
@ -55,11 +51,13 @@
|
|||
"react-markdown": "^9.0.1",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"tsup": "^8.0.1",
|
||||
"tsup": "^8.0.2",
|
||||
"typescript": "^5.3.3",
|
||||
"vavite": "^4.0.1",
|
||||
"vike": "^0.4.156",
|
||||
"vite": "^5.0.11",
|
||||
"vavite": "^4.0.3",
|
||||
"vike": "^0.4.161",
|
||||
"vite": "^5.1.1",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-plugin-graphql-codegen": "^3.3.6",
|
||||
"yup": "^1.3.3"
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,5 @@
|
|||
import React, { FC, Fragment } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import React, { Fragment } from 'react';
|
||||
import { Header } from './components/header/header';
|
||||
import { Title } from './components/title';
|
||||
import './styles/globals.css';
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable react/no-danger */
|
||||
import clsx from 'clsx';
|
||||
import * as avatar from 'generate-avatar';
|
||||
import type { FC } from 'react';
|
||||
|
@ -14,7 +13,7 @@ export const Avatar: FC<AvatarProps> = (props) => {
|
|||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const svg = useMemo(() => {
|
||||
const result = avatar.generateFromString(props.userId);
|
||||
return result.replace(/(width|height)="(\d+)"/g, '$1="100%"');
|
||||
return result.replaceAll(/(width|height)="(\d+)"/g, '$1="100%"');
|
||||
}, [props.userId]);
|
||||
|
||||
return <div className={classes} dangerouslySetInnerHTML={{ __html: svg }} ref={containerRef} />;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable react/button-has-type */
|
||||
import clsx from 'clsx';
|
||||
import type { FC, HTMLAttributes } from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { FC } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { getErrorMessage } from '../helpers/get-error-message.helper';
|
||||
import { usePaths } from '../hooks/usePaths';
|
||||
import { Container } from './container';
|
||||
|
|
|
@ -12,7 +12,7 @@ import { Input } from '../input/input';
|
|||
import { Link } from '../link';
|
||||
import { useToasts } from '../toast';
|
||||
import { HeaderUser } from './header-user';
|
||||
import { graphql } from '../../@generated';
|
||||
import { graphql } from '../../@generated/gql';
|
||||
import { useMutation } from '@urql/preact';
|
||||
|
||||
const ResendVerificationEmail = graphql(`
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { ComponentProps } from 'react';
|
||||
import React from 'react';
|
||||
import type { InputChildProps } from './container';
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { ComponentProps } from 'react';
|
||||
import type { ComponentProps } from 'react';
|
||||
import React from 'react';
|
||||
import type { InputChildProps } from './container';
|
||||
import { InputContainer } from './container';
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { useFormikContext } from 'formik';
|
||||
import type { FC } from 'react';
|
||||
import { Button, ButtonProps } from '../button';
|
||||
import type { ButtonProps } from '../button';
|
||||
import { Button } from '../button';
|
||||
|
||||
/**
|
||||
* Wraps a button and disables when the form is not ready to be submitted.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { ComponentProps, forwardRef } from 'react';
|
||||
import type { ComponentProps} from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
export interface LinkProps extends ComponentProps<'a'> {
|
||||
href: string;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import clsx from 'clsx';
|
||||
import { FC, Fragment, ReactNode, memo } from 'react';
|
||||
import type { FC, ReactNode } from 'react';
|
||||
import { Fragment, memo } from 'react';
|
||||
import { BASE_BUTTON_CLASSES } from './button';
|
||||
import { BASE_INPUT_CLASSES, BASE_INPUT_MAX_HEIGHT } from './input/container';
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { ToastProps } from './toast';
|
||||
import type { ToastProps } from './toast';
|
||||
|
||||
export type ToastContextData = null | ((toast: ToastProps) => void);
|
||||
export const ToastContext = React.createContext<ToastContextData>(null);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import clsx from 'clsx';
|
||||
import { type FC, type ReactNode } from 'react';
|
||||
import type { FC, ReactNode } from 'react';
|
||||
import { FiInfo } from 'react-icons/fi';
|
||||
|
||||
export const Warning: FC<{ children: ReactNode; className?: string }> = ({ children, className }) => {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import clsx from 'clsx';
|
||||
import { FC, Fragment, useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { Fragment, useState } from 'react';
|
||||
import { FiDownload } from 'react-icons/fi';
|
||||
import { RegularUserFragment } from '../../@generated/graphql';
|
||||
import type { RegularUserFragment } from '../../@generated/graphql';
|
||||
import { Container } from '../../components/container';
|
||||
import { Section } from '../../components/section';
|
||||
import { Skeleton, SkeletonList, SkeletonWrap } from '../../components/skeleton';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { memo, useEffect, useMemo, useState } from 'react';
|
||||
import { FiFileMinus, FiTrash } from 'react-icons/fi';
|
||||
import { graphql } from '../../../@generated';
|
||||
import { FileCardFragment } from '../../../@generated/graphql';
|
||||
import { graphql } from '../../../@generated/gql';
|
||||
import type { FileCardFragment } from '../../../@generated/graphql';
|
||||
import { Link } from '../../../components/link';
|
||||
import { Skeleton } from '../../../components/skeleton';
|
||||
import { useConfig } from '../../../hooks/useConfig';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import clsx from 'clsx';
|
||||
import { memo } from 'react';
|
||||
import { graphql } from '../../../@generated';
|
||||
import { PasteCardFragment } from '../../../@generated/graphql';
|
||||
import { graphql } from '../../../@generated/gql';
|
||||
import type { PasteCardFragment } from '../../../@generated/graphql';
|
||||
import { Link } from '../../../components/link';
|
||||
import { Time } from '../../../components/time';
|
||||
import { useUser } from '../../../hooks/useUser';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useQuery } from '@urql/preact';
|
||||
import type { FC } from 'react';
|
||||
import { Fragment } from 'react';
|
||||
import { graphql } from '../../@generated';
|
||||
import { graphql } from '../../@generated/gql';
|
||||
import { Breadcrumbs } from '../../components/breadcrumbs';
|
||||
import { Card } from '../../components/card';
|
||||
import { Error } from '../../components/error';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { memo } from 'react';
|
||||
import { IconType } from 'react-icons/lib';
|
||||
import type { IconType } from 'react-icons/lib';
|
||||
|
||||
interface MissingPreviewProps {
|
||||
icon: IconType;
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Form, Formik } from 'formik';
|
|||
import type { FC } from 'react';
|
||||
import { Fragment, useCallback, useEffect, useState } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { LoginMutationVariables } from '../@generated/graphql';
|
||||
import type { LoginMutationVariables } from '../@generated/graphql';
|
||||
import { Input } from '../components/input/input';
|
||||
import { OtpInput } from '../components/input/otp';
|
||||
import { Submit } from '../components/input/submit';
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { CombinedError, useQuery } from '@urql/preact';
|
||||
import { graphql } from '../@generated';
|
||||
import type { CombinedError } from '@urql/preact';
|
||||
import { useQuery } from '@urql/preact';
|
||||
import { graphql } from '../@generated/gql';
|
||||
|
||||
const ConfigQuery = graphql(`
|
||||
query Config {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import { useConfig } from './useConfig';
|
||||
|
||||
export const usePaths = () => {
|
||||
|
|
|
@ -8,8 +8,7 @@ export const useQueryState = <S>(key: string, initialState?: S, parser?: (input:
|
|||
// during SSR, we can grab query params from the page context
|
||||
const value = pageContext.urlParsed.search[key];
|
||||
if (value) {
|
||||
const result = parser ? parser(value) : (value as any);
|
||||
return result;
|
||||
return parser ? parser(value) : (value as any);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,8 +17,7 @@ export const useQueryState = <S>(key: string, initialState?: S, parser?: (input:
|
|||
const search = new URLSearchParams(window.location.search);
|
||||
const value = search.get(key);
|
||||
if (value) {
|
||||
const result = parser ? parser(value) : (value as any);
|
||||
return result;
|
||||
return parser ? parser(value) : (value as any);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { CombinedError, TypedDocumentNode, useMutation, useQuery } from '@urql/preact';
|
||||
import { graphql } from '../@generated';
|
||||
import type { GetUserQuery, LoginMutationVariables, RegularUserFragment } from '../@generated/graphql';
|
||||
import type { CombinedError, TypedDocumentNode } from '@urql/preact';
|
||||
import { useMutation, useQuery } from '@urql/preact';
|
||||
import { graphql } from '../@generated/gql';
|
||||
import type { GetUserQuery, LoginMutationVariables } from '../@generated/graphql';
|
||||
import { navigate, reload } from '../helpers/routing';
|
||||
import { type RegularUserFragment } from '../@generated/graphql';
|
||||
import { useAsync } from './useAsync';
|
||||
|
||||
const RegularUserFragment = graphql(`
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { FC } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { Container } from '../components/container';
|
||||
import { useConfig } from '../hooks/useConfig';
|
||||
import { Spinner } from '../components/spinner';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { FC } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { Container } from '../../components/container';
|
||||
import { Title } from '../../components/title';
|
||||
import { FileList } from '../../containers/file-list/file-list';
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { useMutation, useQuery } from '@urql/preact';
|
||||
import clsx from 'clsx';
|
||||
import { QRCodeSVG } from 'qrcode.react';
|
||||
import { FC, Fragment, useCallback, useMemo } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { Fragment, useCallback, useMemo } from 'react';
|
||||
import { FiChevronLeft, FiChevronRight, FiCopy, FiDownload } from 'react-icons/fi';
|
||||
import { graphql } from '../../../@generated';
|
||||
import { graphql } from '../../../@generated/gql';
|
||||
import { Button, ButtonStyle } from '../../../components/button';
|
||||
import { Container } from '../../../components/container';
|
||||
import { Error } from '../../../components/error';
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { FC, Fragment } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { Fragment } from 'react';
|
||||
import { useMutation, useQuery } from '@urql/preact';
|
||||
import { graphql } from '../../../@generated';
|
||||
import { graphql } from '../../../@generated/gql';
|
||||
import { Breadcrumbs } from '../../../components/breadcrumbs';
|
||||
import { Button } from '../../../components/button';
|
||||
import { Container } from '../../../components/container';
|
||||
|
@ -42,7 +43,6 @@ export const Page: FC = () => {
|
|||
const { logout } = useLogoutUser();
|
||||
const [, refreshMutation] = useMutation(RefreshToken);
|
||||
const [refresh, refreshing] = useAsync(async () => {
|
||||
// eslint-disable-next-line no-alert
|
||||
const confirmation = confirm('Are you sure? This will invalidate all existing configs and sessions and will sign you out of the dashboard.') // prettier-ignore
|
||||
if (!confirmation) return;
|
||||
await refreshMutation({});
|
||||
|
|
|
@ -4,7 +4,7 @@ import type { FC, ReactNode } from 'react';
|
|||
import { Fragment, useState } from 'react';
|
||||
import { FiDownload, FiShare, FiTrash } from 'react-icons/fi';
|
||||
import { useMutation, useQuery } from '@urql/preact';
|
||||
import { graphql } from '../../../@generated';
|
||||
import { graphql } from '../../../@generated/gql';
|
||||
import { Container } from '../../../components/container';
|
||||
import { Embed } from '../../../components/embed/embed';
|
||||
import { Error } from '../../../components/error';
|
||||
|
@ -16,7 +16,7 @@ import { downloadUrl } from '../../../helpers/download.helper';
|
|||
import { navigate } from '../../../helpers/routing';
|
||||
import { useAsync } from '../../../hooks/useAsync';
|
||||
import { useQueryState } from '../../../hooks/useQueryState';
|
||||
import { PageProps } from '../../../renderer/types';
|
||||
import type { PageProps } from '../../../renderer/types';
|
||||
|
||||
const GetFile = graphql(`
|
||||
query GetFile($fileId: ID!) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { FC, useEffect } from 'react';
|
||||
import { graphql } from '../../../@generated';
|
||||
import type { FC } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { graphql } from '../../../@generated/gql';
|
||||
import { Container } from '../../../components/container';
|
||||
import { Error } from '../../../components/error';
|
||||
import { PageLoader } from '../../../components/page-loader';
|
||||
|
@ -12,7 +13,7 @@ import { getErrorMessage } from '../../../helpers/get-error-message.helper';
|
|||
import { navigate, prefetch } from '../../../helpers/routing';
|
||||
import { useAsync } from '../../../hooks/useAsync';
|
||||
import { useConfig } from '../../../hooks/useConfig';
|
||||
import { PageProps } from '../../../renderer/types';
|
||||
import type { PageProps } from '../../../renderer/types';
|
||||
import { useQuery, useMutation } from '@urql/preact';
|
||||
|
||||
const GetInvite = graphql(`
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { FC, useEffect } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { Title } from '../../components/title';
|
||||
import { LoginForm } from '../../containers/login-form';
|
||||
import { Container } from '../../components/container';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Form, Formik } from 'formik';
|
||||
import { FC } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { graphql } from '../../@generated';
|
||||
import { graphql } from '../../@generated/gql';
|
||||
import type { CreatePasteDto } from '../../@generated/graphql';
|
||||
import { Button } from '../../components/button';
|
||||
import { Container } from '../../components/container';
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { FC, useEffect, useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { FiBookOpen, FiClock, FiTrash } from 'react-icons/fi';
|
||||
import { graphql } from '../../../@generated';
|
||||
import { graphql } from '../../../@generated/gql';
|
||||
import { Button } from '../../../components/button';
|
||||
import { Container } from '../../../components/container';
|
||||
import { Embed } from '../../../components/embed/embed';
|
||||
|
@ -12,7 +13,7 @@ import { decryptContent } from '../../../helpers/encrypt.helper';
|
|||
import { hashToObject } from '../../../helpers/hash-to-object';
|
||||
import { navigate } from '../../../helpers/routing';
|
||||
import { useUser } from '../../../hooks/useUser';
|
||||
import { PageProps } from '../../../renderer/types';
|
||||
import type { PageProps } from '../../../renderer/types';
|
||||
import { useQuery } from '@urql/preact';
|
||||
|
||||
const PasteQuery = graphql(`
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { useMutation } from '@urql/preact';
|
||||
import { Form, Formik } from 'formik';
|
||||
import { FC, useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { useState } from 'react';
|
||||
import * as Yup from 'yup';
|
||||
import { graphql } from '../../@generated';
|
||||
import { graphql } from '../../@generated/gql';
|
||||
import { Button } from '../../components/button';
|
||||
import { Container } from '../../components/container';
|
||||
import { Input } from '../../components/input/input';
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Config } from 'vike/types';
|
||||
import type { Config } from 'vike/types';
|
||||
|
||||
export default {
|
||||
passToClient: ['state', 'routeParams'],
|
||||
|
|
|
@ -2,7 +2,7 @@ import { createClient, fetchExchange, ssrExchange } from '@urql/preact';
|
|||
import { Provider as UrqlProvider } from '@urql/preact';
|
||||
import { hydrate } from 'preact';
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
import { OnRenderClientAsync } from 'vike/types';
|
||||
import type { OnRenderClientAsync } from 'vike/types';
|
||||
import { App } from '../app';
|
||||
import { PageContextProvider } from './usePageContext';
|
||||
import { cacheOptions } from './cache';
|
||||
|
@ -37,6 +37,6 @@ export const onRenderClient: OnRenderClientAsync = async (pageContext) => {
|
|||
</HelmetProvider>
|
||||
</UrqlProvider>
|
||||
</PageContextProvider>,
|
||||
document.getElementById('root')!,
|
||||
document.querySelector('#root')!,
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { cacheExchange } from '@urql/exchange-graphcache';
|
||||
import { Provider as UrqlProvider, createClient, fetchExchange, ssrExchange } from '@urql/preact';
|
||||
import { HelmetProvider, HelmetServerState } from 'react-helmet-async';
|
||||
import type { HelmetServerState } from 'react-helmet-async';
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
import { dangerouslySkipEscape, escapeInject } from 'vike/server';
|
||||
import type { OnRenderHtmlAsync } from 'vike/types';
|
||||
import { App } from '../app';
|
||||
import { cacheOptions } from './cache';
|
||||
import { renderToStringWithData } from './prepass';
|
||||
import { PageProps } from './types';
|
||||
import type { PageProps } from './types';
|
||||
import { PageContextProvider } from './usePageContext';
|
||||
|
||||
const GRAPHQL_URL = (import.meta.env.PUBLIC_ENV__FRONTEND_API_URL || import.meta.env.FRONTEND_API_URL) + '/graphql';
|
||||
|
@ -43,7 +44,7 @@ export const onRenderHtml: OnRenderHtmlAsync = async (pageContext): ReturnType<O
|
|||
const helmet = helmetContext.helmet!;
|
||||
const documentHtml = escapeInject`<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<Helmet>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
${dangerouslySkipEscape(helmet.title.toString())}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import { CacheExchangeOpts } from '@urql/exchange-graphcache';
|
||||
import type { CacheExchangeOpts } from '@urql/exchange-graphcache';
|
||||
import { relayPagination } from '@urql/exchange-graphcache/extras';
|
||||
|
||||
import schema from '../@generated/introspection.json';
|
||||
|
||||
export const cacheOptions: Partial<CacheExchangeOpts> = {
|
||||
schema: schema,
|
||||
resolvers: {
|
||||
User: {
|
||||
files: relayPagination(),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { VNode } from 'preact';
|
||||
import type { VNode } from 'preact';
|
||||
import renderToString from 'preact-render-to-string';
|
||||
import { Client } from '@urql/preact';
|
||||
import type { Client } from '@urql/preact';
|
||||
|
||||
const MAX_DEPTH = 3;
|
||||
const isPromiseLike = (value: unknown): value is Promise<unknown> => {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { FC } from 'react';
|
||||
import { SSRData } from '@urql/preact';
|
||||
import type { FC } from 'react';
|
||||
import type { SSRData } from '@urql/preact';
|
||||
|
||||
// https://vike.dev/pageContext#typescript
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
declare global {
|
||||
namespace Vike {
|
||||
interface PageContext {
|
||||
|
|
|
@ -14,6 +14,5 @@ export function PageContextProvider({
|
|||
}
|
||||
|
||||
export function usePageContext() {
|
||||
const pageContext = useContext(Context);
|
||||
return pageContext;
|
||||
return useContext(Context);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import FastifyEarlyHints from '@fastify/early-hints';
|
||||
import FastifyProxy from '@fastify/http-proxy';
|
||||
import Fastify, { FastifyInstance } from 'fastify';
|
||||
import { IncomingMessage, ServerResponse } from 'http';
|
||||
import type { FastifyInstance } from 'fastify';
|
||||
import Fastify from 'fastify';
|
||||
import type { IncomingMessage, ServerResponse } from 'http';
|
||||
import { compile, match } from 'path-to-regexp';
|
||||
import url from 'url';
|
||||
import { renderPage } from 'vike/server';
|
||||
import { PageContext } from 'vike/types';
|
||||
import type { PageContext } from 'vike/types';
|
||||
import { REWRITES } from './rewrites';
|
||||
|
||||
const rewrites = REWRITES.map(({ source, destination }) => ({
|
||||
|
@ -58,8 +59,7 @@ async function startServer() {
|
|||
for (const { match, toPath } of rewrites) {
|
||||
const result = match(pathname);
|
||||
if (result) {
|
||||
const replaced = toPath(result.params);
|
||||
return replaced;
|
||||
return toPath(result.params);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,14 +99,14 @@ async function startServer() {
|
|||
}
|
||||
|
||||
const { httpResponse } = pageContext;
|
||||
if (!httpResponse) {
|
||||
reply.status(500).send('Internal Server Error');
|
||||
} else {
|
||||
if (httpResponse) {
|
||||
const { body, statusCode, headers, earlyHints } = httpResponse;
|
||||
reply.writeEarlyHints({ link: earlyHints.map((e) => e.earlyHintLink) });
|
||||
headers.forEach(([name, value]) => reply.header(name, value));
|
||||
for (const [name, value] of headers) reply.header(name, value);
|
||||
reply.status(statusCode);
|
||||
reply.send(body);
|
||||
} else {
|
||||
reply.status(500).send('Internal Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -115,6 +115,7 @@ async function startServer() {
|
|||
}
|
||||
|
||||
let fastify: FastifyInstance | undefined;
|
||||
// eslint-disable-next-line unicorn/prefer-top-level-await
|
||||
const fastifyHandlerPromise = startServer().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
{
|
||||
"extends": "@atlasbot/configs/tsconfig/esm.json",
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"module": "ES2022",
|
||||
"target": "ES2022",
|
||||
"moduleResolution": "Bundler",
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"types": ["vite/client"],
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"noUnusedLocals": true,
|
||||
|
||||
"noUncheckedIndexedAccess": false,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "preact",
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"react": ["./node_modules/preact/compat/"],
|
||||
"react-dom": ["./node_modules/preact/compat/"],
|
||||
// todo: https://github.com/gxmari007/vite-plugin-eslint/issues/74
|
||||
"vite-plugin-eslint": ["./node_modules/vite-plugin-eslint/dist/index.d.ts"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -2,11 +2,14 @@ import { preact } from '@preact/preset-vite';
|
|||
import { vavite } from 'vavite';
|
||||
import ssr from 'vike/plugin';
|
||||
import { defineConfig } from 'vite';
|
||||
import eslint from 'vite-plugin-eslint';
|
||||
import codegen from 'vite-plugin-graphql-codegen';
|
||||
|
||||
// todo: https://github.com/dotansimha/graphql-code-generator/issues/9774
|
||||
export default defineConfig({
|
||||
buildSteps: [
|
||||
{ name: 'client' },
|
||||
{
|
||||
name: 'client',
|
||||
},
|
||||
{
|
||||
name: 'server',
|
||||
config: {
|
||||
|
@ -21,11 +24,14 @@ export default defineConfig({
|
|||
noExternal: ['react-helmet-async', 'prism-react-renderer', 'qrcode.react', 'formik'],
|
||||
},
|
||||
plugins: [
|
||||
codegen(),
|
||||
eslint({ cache: true }),
|
||||
preact(),
|
||||
ssr({ disableAutoFullBuild: true }),
|
||||
vavite({
|
||||
handlerEntry: '/src/server/index.ts',
|
||||
serveClientAssetsInDev: true,
|
||||
clientAssetsDir: 'dist/client',
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
|
1601
pnpm-lock.yaml
1601
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
42
turbo.json
42
turbo.json
|
@ -1,26 +1,20 @@
|
|||
{
|
||||
"baseBranch": "origin/main",
|
||||
"pipeline": {
|
||||
"build": {
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"outputs": [
|
||||
"dist/**",
|
||||
".next/{server,cache,src,static}"
|
||||
]
|
||||
},
|
||||
"lint": {
|
||||
"outputs": []
|
||||
},
|
||||
"test": {
|
||||
"outputs": []
|
||||
},
|
||||
"clean": {
|
||||
"outputs": []
|
||||
},
|
||||
"watch": {
|
||||
"cache": false
|
||||
}
|
||||
"pipeline": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["dist/**", ".next/{server,cache,src,static}"]
|
||||
},
|
||||
"lint": {
|
||||
"outputs": []
|
||||
},
|
||||
"test": {
|
||||
"outputs": []
|
||||
},
|
||||
"clean": {
|
||||
"outputs": []
|
||||
},
|
||||
"watch": {
|
||||
"cache": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue