Skip to content

Commit

Permalink
⭐add declaration of schema
Browse files Browse the repository at this point in the history
  • Loading branch information
etefaghian committed May 25, 2021
1 parent 9b46d50 commit da1c155
Show file tree
Hide file tree
Showing 15 changed files with 281 additions and 37 deletions.
45 changes: 8 additions & 37 deletions declarations/request/mod.ts
Original file line number Diff line number Diff line change
@@ -1,50 +1,21 @@
import { Project, ts, log } from "../../deps.ts";
import { Project, log } from "../../deps.ts";
import { ensureDir } from "../../deps.ts";
import {
jsonObToTsType,
convertFvObToTsOb,
constructFVSchema,
} from "./utils/mod.ts";
import { rgb24 } from "https://deno.land/[email protected]/fmt/colors.ts";
import { denoResolutionHost } from "../utils/mod.ts";

export const getRequestDeclarations = async (dirPath?: string) => {
log.info("Generating of declarations is started");
log.info("Generating of declarations of request is started");
const project = new Project({
resolutionHost: (moduleResolutionHost, getCompilerOptions) => {
return {
resolveModuleNames: (moduleNames, containingFile) => {
const compilerOptions = getCompilerOptions();
const resolvedModules: ts.ResolvedModule[] = [];

for (const moduleName of moduleNames.map(removeTsExtension)) {
const result = ts.resolveModuleName(
moduleName,
containingFile,
compilerOptions,
moduleResolutionHost
);
resolvedModules.push(result.resolvedModule!);
}

return resolvedModules;
},
};

function removeTsExtension(moduleName: string) {
if (
moduleName.slice(-3).toLowerCase() === ".ts" &&
!moduleName.startsWith("http")
) {
return moduleName.slice(0, -3);
}
return moduleName;
}
},
resolutionHost: denoResolutionHost,
});

const __dirname = dirPath || Deno.cwd();

await ensureDir("declarations");
await ensureDir("declarations/request");

project.addSourceFilesAtPaths(`${__dirname}/**/*.ts`);
Expand Down Expand Up @@ -88,13 +59,13 @@ export const getRequestDeclarations = async (dirPath?: string) => {
newSourceFile.formatText({ indentSize: 1 });
//save new interface
await newSourceFile.save();
log.info(`creating of declaration files was successful
log.info(`creating of declaration files for request was successful
${rgb24(
`
-------------------------------------------------------------
| Fastest validator schema: file:///${__dirname}/declarations/request/fastestValidatorSchema.json
| Json schema: file:///${__dirname}/declarations/request/schema.json
| Ts interface: file:///${__dirname}/declarations/request/schema.ts
| Fastest validator schema: file://${__dirname}/declarations/request/fastestValidatorSchema.json
| Json schema: file://${__dirname}/declarations/request/schema.json
| Ts interface: file://${__dirname}/declarations/request/schema.ts
-------------------------------------------------------------
`,
0xd257ff
Expand Down
62 changes: 62 additions & 0 deletions declarations/schema/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Project, log } from "../../deps.ts";
import { ensureDir } from "../../deps.ts";
import { rgb24 } from "https://deno.land/[email protected]/fmt/colors.ts";
import { denoResolutionHost } from "../utils/mod.ts";
import { addFunQLInterfaceToSourceFile } from "./utils/addInterfaceToSrcFile.ts";

export const getSchemaDeclarations = async (dirPath?: string) => {
log.info("Generating of declarations of schema is started");
const project = new Project({
resolutionHost: denoResolutionHost,
});

const __dirname = dirPath || Deno.cwd();
await ensureDir("declarations/schema");
project.addSourceFilesAtPaths(`${__dirname}/**/*.ts`);
//handle differentiate between schema and schemas
const dir =
project.getDirectory(`${__dirname}/schema`) ||
project.getDirectory(`${__dirname}/schemas`);

const createdSourceFile = project.createSourceFile(
`${__dirname}/declarations/schema/schema.ts`,
undefined,
{
overwrite: true,
}
);

const sourceFiles = dir?.getSourceFiles();

//get all of interfaces
sourceFiles?.map((sourceFile) => {
const selectedInterfaces = sourceFile
.getInterfaces()
.filter(
(inter) =>
inter.getName().startsWith("Pu") ||
inter.getName().startsWith("Em") ||
inter.getName().startsWith("In") ||
inter.getName().startsWith("OutRel") ||
inter.getName().startsWith("I")
);

selectedInterfaces.map((inter) =>
addFunQLInterfaceToSourceFile(inter, createdSourceFile)
);
});

//console.log(newSourceFile.getText());
await createdSourceFile.save();

log.info(`creating of declaration files for schema was successful
${rgb24(
`
-------------------------------------------------------------
| schema: file://${__dirname}/declarations/schema/schema.ts
-------------------------------------------------------------
`,
0xadfc03
)}
`);
};
23 changes: 23 additions & 0 deletions declarations/schema/utils/addEnumToSrcFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { EnumDeclaration, SourceFile } from "../../../deps.ts";
import { changeNameAndItsRefs } from "./ts-morph/mod.ts";

/**
* @function
* add an enum to specified sourcefile and also rename it to be unique
* @param myInterface the interface that we want to add it to sourcefile
* @param createdSourceFile reference of created sourcefile that we want to add the interface to it
*/
export function addFunQLEnumToSourceFile(
myEnum: EnumDeclaration,
createdSourceFile: SourceFile
) {
//checks enum name is duplicate or not
if (myEnum.getName().startsWith("FunQl")) {
return;
} else {
//change name of interface first
changeNameAndItsRefs(myEnum);
}
//even enum is not exported we export it
createdSourceFile.addEnum({ ...myEnum.getStructure(), isExported: true });
}
51 changes: 51 additions & 0 deletions declarations/schema/utils/addInterfaceToSrcFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { SourceFile, InterfaceDeclaration } from "../../../deps.ts";
import { isInternalType, handlePropType } from "./mod.ts";
import {
changeNameAndItsRefs,
findAllPropsOfInterface,
} from "./ts-morph/mod.ts";

/**
* @function
* add an interface to specified sourcefile with all dependencies and also rename it to be unique
* @param myInterface the interface that we want to add it to sourcefile
* @param createdSourceFile reference of created sourcefile that we want to add the interface to it
*/
export function addFunQLInterfaceToSourceFile(
myInterface: InterfaceDeclaration,
createdSourceFile: SourceFile
) {
//checks interface name is duplicate or not and also is Date or not
if (
//ignore date type
isInternalType(myInterface.getName()) ||
//when interface was inserted to source file
myInterface.getName().startsWith("FunQl")
) {
return;
} else {
//change name of interface first
changeNameAndItsRefs(myInterface);
}

//create new interface with new name
const createdInterface = createdSourceFile.addInterface({
name: myInterface.getName(),
isExported: true,
});

//finds all props that include in body of interface or specified with inheritance
const foundedProps = findAllPropsOfInterface(myInterface);

for (const prop of foundedProps) {
//handle when type of prop is Bson.ObjectId
//map it to string type
if (prop.getText().match(/Bson.ObjectI[dD]/)) {
prop.setType("string");
}
//construct deps of interface in prop
handlePropType(prop, createdSourceFile);
//add prop to created interface
createdInterface.addProperty(prop.getStructure());
}
}
32 changes: 32 additions & 0 deletions declarations/schema/utils/handlePropType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { PropertySignature, SourceFile, SyntaxKind } from "../../../deps.ts";
import { getInterfaceFromType, getEnumFromType } from "./ts-morph/mod.ts";
import { addFunQLInterfaceToSourceFile } from "./addInterfaceToSrcFile.ts";
import { addFunQLEnumToSourceFile } from "./mod.ts";

/**
* @function
* find and add associated declaration to this type to source file
* @param type
* @param createdSourceFile
*/
export function handlePropType(
prop: PropertySignature,
createdSourceFile: SourceFile
) {
const typeReferences = prop.getDescendantsOfKind(SyntaxKind.TypeReference);
typeReferences.map((reference) => {
//get type of references
const typeOfReference = reference.getType();

//if type is interface we should find interface and process it again
if (typeOfReference.isInterface()) {
const foundedInterface = getInterfaceFromType(typeOfReference);
addFunQLInterfaceToSourceFile(foundedInterface, createdSourceFile);
}
//if type is enum we should find enum and add it to source file
if (typeOfReference.isEnum()) {
const foundedEnum = getEnumFromType(typeOfReference);
addFunQLEnumToSourceFile(foundedEnum, createdSourceFile);
}
});
}
8 changes: 8 additions & 0 deletions declarations/schema/utils/isTypeInternal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* specifies whether this type is belongs to internal js type or not
* @param typeName name of type
* @todo should add new name of types
*/
export function isInternalType(typeName: string) {
return typeName === "Date";
}
4 changes: 4 additions & 0 deletions declarations/schema/utils/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./addEnumToSrcFile.ts";
export * from "./addEnumToSrcFile.ts";
export * from "./handlePropType.ts";
export * from "./isTypeInternal.ts";
25 changes: 25 additions & 0 deletions declarations/schema/utils/ts-morph/changeNameAndRef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {
InterfaceDeclaration,
EnumDeclaration,
TypeAliasDeclaration,
} from "../../../../deps.ts";

/**
* @function
* change name of a node and its references
* @param node the node that we want to rename it and own references
*/
export function changeNameAndItsRefs(
node: InterfaceDeclaration | EnumDeclaration | TypeAliasDeclaration
) {
//we use split for remove some postfixes for example authority.embedded.ts
const fileName = node
.getSourceFile()
.getBaseNameWithoutExtension()
.split(".")[0];
//generate new name
const newName = `FunQl_${node.getName()}_${fileName}`;

//rename node and its refs
node.rename(newName, { renameInStrings: true, usePrefixAndSuffixText: true });
}
16 changes: 16 additions & 0 deletions declarations/schema/utils/ts-morph/findInterfaceProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { InterfaceDeclaration, PropertySignature } from "../../../../deps.ts";

/**
* @function
* finds all props of an interface includes extends and ....
*/
export function findAllPropsOfInterface(
myInterface: InterfaceDeclaration
): PropertySignature[] {
return <PropertySignature[]>myInterface
.getSymbolOrThrow()
.getDeclarations()[0]
.getType()
.getProperties()
.map((prop) => prop.getDeclarations()[0]);
}
5 changes: 5 additions & 0 deletions declarations/schema/utils/ts-morph/getEnumFromType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Type, EnumDeclaration } from "../../../../deps.ts";

export function getEnumFromType(type: Type) {
return <EnumDeclaration>type.getSymbolOrThrow().getDeclarations()[0];
}
5 changes: 5 additions & 0 deletions declarations/schema/utils/ts-morph/getInterfaceFromType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { InterfaceDeclaration, Type } from "../../../../deps.ts";

export function getInterfaceFromType(type: Type) {
return <InterfaceDeclaration>type.getSymbolOrThrow().getDeclarations()[0];
}
4 changes: 4 additions & 0 deletions declarations/schema/utils/ts-morph/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./changeNameAndRef.ts";
export * from "./findInterfaceProps.ts";
export * from "./getEnumFromType.ts";
export * from "./getInterfaceFromType.ts";
35 changes: 35 additions & 0 deletions declarations/utils/denoResolutionHost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ResolutionHostFactory, ts } from "../../deps.ts";

export const denoResolutionHost: ResolutionHostFactory = (
moduleResolutionHost,
getCompilerOptions
) => {
return {
resolveModuleNames: (moduleNames, containingFile) => {
const compilerOptions = getCompilerOptions();
const resolvedModules: ts.ResolvedModule[] = [];

for (const moduleName of moduleNames.map(removeTsExtension)) {
const result = ts.resolveModuleName(
moduleName,
containingFile,
compilerOptions,
moduleResolutionHost
);
resolvedModules.push(result.resolvedModule!);
}

return resolvedModules;
},
};

function removeTsExtension(moduleName: string) {
if (
moduleName.slice(-3).toLowerCase() === ".ts" &&
!moduleName.startsWith("http")
) {
return moduleName.slice(0, -3);
}
return moduleName;
}
};
1 change: 1 addition & 0 deletions declarations/utils/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./denoResolutionHost.ts";
2 changes: 2 additions & 0 deletions funql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getRequestDeclarations } from "./declarations/request/mod.ts";
import "./config/mod.ts";
import { upgrade } from "./cli/mod.ts";
import { log } from "./deps.ts";
import { getSchemaDeclarations } from "./declarations/schema/mod.ts";

export interface CommandArgs {
init?: boolean | string;
Expand All @@ -23,5 +24,6 @@ const createProject = async (init: string | boolean) => {

args.init && (await createProject(args.init));
args.declaration && (await getRequestDeclarations());
args.declaration && (await getSchemaDeclarations());

args.upgrade && (await upgrade(args.upgrade));

0 comments on commit da1c155

Please sign in to comment.