Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 198 additions & 0 deletions integration/core/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import { Typescript } from "@duplojs/data-parser-tools";
import { resolveTsconfig } from "@duplojs/http/codeGenerator";
import { getCurrentWorkDirectoryOrThrow, SF } from "@duplojs/server-utils";
import { A, type AnyTuple, asyncPipe, E, G, innerPipe, isType, justExec, O, Path, pipe, promiseAll, S } from "@duplojs/utils";

const tsconfig = E.unwrapByInformationOrThrow(
resolveTsconfig(
getCurrentWorkDirectoryOrThrow(),
"core/tsconfig.json",
),
"success",
);

const compilerOptions = {
...tsconfig.options,
noEmit: false,
declaration: true,
emitDeclarationOnly: true,
declarationDir: "core/temp",
};

await SF.remove(compilerOptions.declarationDir, { recursive: true });

const program = Typescript.createProgram({
rootNames: tsconfig.fileNames,
options: compilerOptions,
projectReferences: tsconfig.projectReferences,
});

const checker = program.getTypeChecker();

let routeIndex = 0;
const printer = Typescript.createPrinter();
const virtualFiles = await asyncPipe(
tsconfig.fileNames,
A.chunk(10),
G.asyncMap(
innerPipe(
A.map((fileName) => {
const sourceFile = program.getSourceFile(fileName);
if (!sourceFile) {
return null;
}

const newStatement = A.flatMap(
sourceFile.statements,
(statement) => {
if (
!Typescript.isExpressionStatement(statement)
|| !Typescript.isCallExpression(statement.expression)
|| !Typescript.isPropertyAccessExpression(statement.expression.expression)
|| statement.expression.expression.name.text !== "handler"
) {
return statement;
}

const signature = checker.getResolvedSignature(statement.expression);
if (!signature) {
return statement;
}

const returnType = checker.getReturnTypeOfSignature(signature);
const typeSymbol = returnType.getSymbol();
if (!typeSymbol) {
return statement;
}
if (typeSymbol.getName() !== "Route") {
return statement;
}

const typeIdentifier = Typescript.factory.createIdentifier(
`Route_${routeIndex++}`,
);

return [
Typescript.factory.createVariableStatement(
undefined,
Typescript.factory.createVariableDeclarationList(
[
Typescript.factory.createVariableDeclaration(
typeIdentifier,
undefined,
undefined,
statement.expression,
),
],
Typescript.NodeFlags.Const,
),
),
Typescript.factory.createTypeAliasDeclaration(
[Typescript.factory.createToken(Typescript.SyntaxKind.ExportKeyword)],
typeIdentifier,
undefined,
Typescript.factory.createImportTypeNode(
Typescript.factory.createLiteralTypeNode(
Typescript.factory.createStringLiteral("@duplojs/http/codeGenerator"),
),
undefined,
Typescript.factory.createIdentifier("RouteToClientRoute"),
[
Typescript.factory.createTypeQueryNode(
typeIdentifier,
undefined,
),
],
false,
),
),
];
},
);

return O.entry(
fileName,
printer.printFile(
Typescript.factory.updateSourceFile(
sourceFile,
newStatement,
),
),
);
}),
promiseAll,
),
),
G.asyncFlat,
G.asyncFilter(isType("array")),
A.from,
(value) => new Map(value),
);

const host = Typescript.createCompilerHost(compilerOptions);
const originalGetSourceFile = host.getSourceFile.bind(host);
host.getSourceFile = (
fileName,
languageVersion,
onError,
shouldCreateNewSourceFile,
) => {
const content = virtualFiles.get(
fileName,
);

if (content !== undefined) {
return Typescript.createSourceFile(
fileName,
content,
languageVersion,
true,
);
}

return originalGetSourceFile(
fileName,
languageVersion,
onError,
shouldCreateNewSourceFile,
);
};

const newProgram = Typescript.createProgram({
rootNames: tsconfig.fileNames,
options: compilerOptions,
projectReferences: tsconfig.projectReferences,
host,
});

// // généré l'index
// // génére une deuxéime fois mais en résolvant le type pars inférence pour le rendre plus simple
// // résoudre le path typescript
// // threeshaker les type pour garder que le néccésaire

const result: any[] = [];

newProgram.emit(
undefined,
(fileName, content) => {
console.log(fileName);

result.push(
justExec(async() => {
const folder = Path.getParentFolderPath(fileName);

if (folder === null) {
return SF.writeTextFile(fileName, content);
}

await SF.makeDirectory(folder, { recursive: true });

return SF.writeTextFile(fileName, content);
}),
);
},
undefined,
true,
);

await promiseAll(result);
1 change: 1 addition & 0 deletions integration/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "integrations",
"license": "ISC",
"type": "module",
"scripts": {
"test:types": "./.commands/test-types.sh"
},
Expand Down
4 changes: 2 additions & 2 deletions scripts/core/process/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export type ProcessSteps = (
declare const SymbolProcessExportValue: unique symbol;

export interface ProcessDefinition {
steps: readonly ProcessSteps[];
options?: Record<string, unknown>;
readonly steps: readonly ProcessSteps[];
readonly options?: Record<string, unknown>;
readonly hooks: readonly HookRouteLifeCycle[];
readonly metadata: readonly Metadata[];
[SymbolProcessExportValue]?: Floor;
Expand Down
7 changes: 2 additions & 5 deletions scripts/core/response/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,11 +255,8 @@ export namespace ResponseContract {
GenericContract["code"],
GenericContract["information"],
{
[Prop in keyof GenericContract["events"]]: [
Extract<Prop, string>,
DP.Output<GenericContract["events"][Prop]>,
]
}[keyof GenericContract["events"]]
[Prop in keyof GenericContract["events"]]: DP.Output<GenericContract["events"][Prop]>
}
>
: GenericContract extends StreamContract
? StreamPredictedResponse<
Expand Down
16 changes: 8 additions & 8 deletions scripts/core/serverSentEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { type MaybePromise, type MillisecondInString, stringToMillisecond } from
import { Stream } from "./stream";

export namespace ServerSentEvents {
export type DefinitionShape = [string, unknown];
export type DefinitionShape = Record<string, unknown>;

export interface SendParams {
id?: string;
Expand All @@ -14,17 +14,17 @@ export namespace ServerSentEvents {
GenericEvents extends DefinitionShape = DefinitionShape,
> extends Stream.StartSendingParams {
send(
...args: GenericEvents extends any
? [
event: GenericEvents[0],
...args: {
[Event in keyof GenericEvents]: [
event: Event,
...(
GenericEvents[1] extends undefined
? [data?: GenericEvents[1]]
: [data: GenericEvents[1]]
GenericEvents[Event] extends undefined
? [data?: GenericEvents[Event]]
: [data: GenericEvents[Event]]
),
params?: SendParams,
]
: never
}[keyof GenericEvents]
): Promise<void>;
readonly lastId: string | null;
}
Expand Down
4 changes: 2 additions & 2 deletions scripts/core/steps/checker.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createCoreLibKind } from "@core/kind";
import { pipe, type Kind } from "@duplojs/utils";
import { type DP, pipe, type Kind } from "@duplojs/utils";
import { type StepKind, stepKind } from "./kind";
import { type Checker } from "@core/checker";
import { type Floor } from "@core/floor";
Expand All @@ -12,7 +12,7 @@ export interface CheckerStepDefinition {
readonly indexing?: string;
input(input: Floor): unknown;
readonly options?: Record<string, unknown> | ((input: any) => Record<string, unknown>);
readonly responseContract: ResponseContract.Contract<ClientErrorResponseCode>;
readonly responseContract: ResponseContract.Contract<ClientErrorResponseCode, string, DP.DataParserEmpty>;
readonly metadata: readonly Metadata[];
}

Expand Down
3 changes: 3 additions & 0 deletions scripts/plugins/codeGenerator/aggregateStepContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ export function aggregateStepContract(
A.filter(stepIdentifier(processStepKind)),
A.filter(
(step) => A.find(
step.definition.metadata,
IgnoreByCodeGeneratorMetadata.is,
) === undefined && A.find(
step.definition.process.definition.metadata,
IgnoreByCodeGeneratorMetadata.is,
) === undefined,
Expand Down
63 changes: 63 additions & 0 deletions scripts/plugins/codeGenerator/byInference/findRouteTypeNodes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Typescript } from "@duplojs/data-parser-tools";
import { SF } from "@duplojs/server-utils";
import { E, unwrap, type AnyTuple } from "@duplojs/utils";

export interface FindRoutesParams {
includesFolders: AnyTuple<string>;
}

export async function findRouteTypeNodes(
program: Typescript.Program,
checker: Typescript.TypeChecker,
params: FindRoutesParams,
) {
const result = new Set<Typescript.TypeNode>();

for (const path of params.includesFolders) {
const maybeWalker = await SF.walkDirectory(path);

if (E.isLeft(maybeWalker)) {
return E.left("failed-to-read-directory", { path });
}

for (const entry of unwrap(maybeWalker)) {
if (!SF.isFileInterface(entry)) {
continue;
}

const programFile = program.getSourceFile(entry.path);

if (programFile === undefined) {
continue;
}

programFile.forEachChild((node) => {
if (
Typescript.isExpressionStatement(node)
&& Typescript.isCallExpression(node.expression)
&& Typescript.isPropertyAccessExpression(node.expression.expression)
&& node.expression.expression.name.text === "handler"
) {
const signature = checker.getResolvedSignature(node.expression);
if (signature === undefined) {
return signature;
}

const returnType = checker.getReturnTypeOfSignature(signature);
const returnTypeNode = checker.typeToTypeNode(
returnType,
node.expression,
Typescript.NodeBuilderFlags.NoTruncation
| Typescript.TypeFormatFlags.InTypeAlias,
);

if (returnTypeNode) {
result.add(returnTypeNode);
}
}
});
}
}

return E.success(result);
}
2 changes: 2 additions & 0 deletions scripts/plugins/codeGenerator/byInference/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./findRouteTypeNodes";
export * from "./resolveTsconfig";
Loading
Loading