-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add complete cache system for loaders & parser (#28)
* add complete cache system for loaders & parser * enable benchmark dry-run on PRs * refactor files organization * add cache-system tests * upgrade to 1.3.4
- Loading branch information
Showing
17 changed files
with
681 additions
and
135 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"name": "ts-gql-plugin", | ||
"version": "1.3.3", | ||
"version": "1.3.4", | ||
"packageManager": "[email protected]", | ||
"license": "MIT", | ||
"main": "./dist/plugin.js", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { GraphQLProjectConfig, loadConfig } from 'graphql-config'; | ||
import { tsGqlExtension } from '../source-update/extension'; | ||
import { checkFileLastUpdate, createCacheSystem } from '../utils/cache-system'; | ||
import { Logger } from '../utils/logger'; | ||
|
||
type CreateCachedGraphQLConfigLoaderOptions = { | ||
directory: string; | ||
graphqlConfigPath?: string; | ||
projectNameRegex: string | undefined; | ||
logger: Logger; | ||
}; | ||
|
||
type CachedGraphQLConfigLoaderValue = { | ||
configFilePath: string; | ||
graphqlProjects: GraphQLProjectConfig[]; | ||
}; | ||
|
||
export type CachedGraphQLConfigLoader = ReturnType< | ||
typeof createCachedGraphQLConfigLoader | ||
>; | ||
|
||
export const defaultProjectName = 'default'; | ||
|
||
export const createCachedGraphQLConfigLoader = ({ | ||
directory, | ||
graphqlConfigPath, | ||
projectNameRegex, | ||
logger, | ||
}: CreateCachedGraphQLConfigLoaderOptions) => | ||
createCacheSystem<CachedGraphQLConfigLoaderValue, null>({ | ||
// TODO debounce | ||
// debounceValue: 5000, | ||
getKeyFromInput: () => '', | ||
create: async () => { | ||
const graphqlConfig = await loadConfig({ | ||
rootDir: directory, | ||
filepath: graphqlConfigPath, | ||
throwOnMissing: true, | ||
throwOnEmpty: true, | ||
extensions: [tsGqlExtension], | ||
}); | ||
if (!graphqlConfig) { | ||
throw new Error('GraphQL config file not found.'); | ||
} | ||
|
||
const graphqlProjectsMap = graphqlConfig.projects; | ||
|
||
if (!(defaultProjectName in graphqlProjectsMap) && !projectNameRegex) { | ||
throw new Error( | ||
'Multiple projects into GraphQL config. You must define projectNameRegex in config.' | ||
); | ||
} | ||
|
||
const graphqlProjects = Object.values(graphqlProjectsMap); | ||
|
||
logger.log(`GraphQL config loaded from ${graphqlConfig.filepath}`); | ||
|
||
graphqlProjects.forEach(({ name, schema }) => | ||
logger.log(`GraphQL project "${name}" schema loaded from ${schema}`) | ||
); | ||
|
||
return { configFilePath: graphqlConfig.filepath, graphqlProjects }; | ||
}, | ||
checkValidity: async (currentItem) => { | ||
const { configFilePath } = await currentItem.value; | ||
|
||
return checkFileLastUpdate(configFilePath, currentItem.dateTime); | ||
}, | ||
sizeLimit: 40, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import ts from 'typescript/lib/tsserverlibrary'; | ||
import { ErrorCatcher } from '../create-error-catcher'; | ||
import { generateTypeFromLiteral } from '../generators/generate-type-from-literal'; | ||
import { DocumentInfos } from '../generators/generate-bottom-content'; | ||
import { createCacheSystem } from '../utils/cache-system'; | ||
import { CachedSchemaLoader, defaultProjectName } from './cached-schema-loader'; | ||
|
||
type CreateCachedLiteralParserOptions = { | ||
cachedSchemaLoader: CachedSchemaLoader; | ||
projectNameRegex: string | undefined; | ||
scriptTarget: ts.ScriptTarget; | ||
errorCatcher: ErrorCatcher; | ||
}; | ||
|
||
export type CachedLiteralParserValue<D extends DocumentInfos = DocumentInfos> = | ||
{ | ||
documentInfos: D; | ||
staticGlobals: string; | ||
} | null; | ||
|
||
type CachedLiteralParserInput = { | ||
literal: string; | ||
filename: string; | ||
initialSource: string; | ||
}; | ||
|
||
export type CachedLiteralParser = ReturnType<typeof createCachedLiteralParser>; | ||
|
||
export const createCachedLiteralParser = ({ | ||
cachedSchemaLoader, | ||
projectNameRegex, | ||
scriptTarget, | ||
errorCatcher, | ||
}: CreateCachedLiteralParserOptions) => { | ||
const getProjectNameFromLiteral = (literal: string) => | ||
projectNameRegex | ||
? (new RegExp(projectNameRegex).exec(literal) ?? [])[0] | ||
: defaultProjectName; | ||
|
||
const getProjectFromLiteral = async (literal: string) => { | ||
const projectName = getProjectNameFromLiteral(literal); | ||
|
||
const project = await cachedSchemaLoader.getItemOrCreate({ | ||
projectName, | ||
}); | ||
if (!project) { | ||
throw new Error(`Project not defined for name "${projectName}"`); | ||
} | ||
|
||
return project; | ||
}; | ||
|
||
const parser = createCacheSystem< | ||
CachedLiteralParserValue, | ||
CachedLiteralParserInput | ||
>({ | ||
getKeyFromInput: (input) => input.literal.replaceAll(/\s/gi, ''), | ||
create: async ({ literal, filename, initialSource }) => { | ||
try { | ||
const project = await getProjectFromLiteral(literal); | ||
|
||
return { | ||
documentInfos: await generateTypeFromLiteral( | ||
literal, | ||
project.schemaDocument, | ||
project.extension.codegenConfig | ||
), | ||
staticGlobals: project.staticGlobals, | ||
}; | ||
} catch (error) { | ||
errorCatcher( | ||
error, | ||
ts.createSourceFile(filename, initialSource, scriptTarget), | ||
initialSource.indexOf(literal), | ||
literal.length | ||
); | ||
return null; | ||
} | ||
}, | ||
checkValidity: async ({ input }) => { | ||
const projectName = getProjectNameFromLiteral(input.literal); | ||
|
||
return await cachedSchemaLoader.checkItemValidity({ | ||
projectName, | ||
}); | ||
}, | ||
}); | ||
|
||
return parser; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import { DocumentNode } from 'graphql'; | ||
import path from 'node:path'; | ||
import { ErrorCatcher } from '../create-error-catcher'; | ||
import { ExtensionConfig } from '../extension-config'; | ||
import { generateTypeFromSchema } from '../generators/generate-type-from-schema'; | ||
import { getProjectExtension } from '../source-update/extension'; | ||
import { checkFileLastUpdate, createCacheSystem } from '../utils/cache-system'; | ||
import { CachedGraphQLConfigLoader } from './cached-graphql-config-loader'; | ||
|
||
type CreateCachedSchemaLoaderOptions = { | ||
cachedGraphQLConfigLoader: CachedGraphQLConfigLoader; | ||
errorCatcher: ErrorCatcher; | ||
}; | ||
|
||
type ProjectInfos = { | ||
schemaFilePath?: string; | ||
schemaDocument: DocumentNode; | ||
staticGlobals: string; | ||
extension: ExtensionConfig; | ||
}; | ||
|
||
type CachedSchemaLoaderValue = ProjectInfos | null; | ||
|
||
type CachedSchemaLoaderInput = { | ||
projectName: string; | ||
}; | ||
|
||
export type CachedSchemaLoader = ReturnType<typeof createCachedSchemaLoader>; | ||
|
||
export const defaultProjectName = 'default'; | ||
|
||
export const createCachedSchemaLoader = ({ | ||
cachedGraphQLConfigLoader, | ||
errorCatcher, | ||
}: CreateCachedSchemaLoaderOptions) => | ||
createCacheSystem<CachedSchemaLoaderValue, CachedSchemaLoaderInput>({ | ||
// TODO debounce | ||
// debounceValue: 1000, | ||
getKeyFromInput: (input) => input.projectName, | ||
create: async ({ projectName }) => { | ||
const { graphqlProjects } = | ||
await cachedGraphQLConfigLoader.getItemOrCreate(null); | ||
|
||
const project = graphqlProjects.find(({ name }) => name === projectName); | ||
if (!project) { | ||
throw new Error(`Project not defined for name "${projectName}"`); | ||
} | ||
|
||
const extension = getProjectExtension(project); | ||
|
||
const schemaFilePath = | ||
typeof project.schema === 'string' | ||
? path.join(project.dirpath, project.schema) | ||
: undefined; | ||
|
||
return project | ||
.getSchema('DocumentNode') | ||
.then( | ||
async (schemaDocument): Promise<ProjectInfos> => ({ | ||
schemaFilePath, | ||
schemaDocument, | ||
staticGlobals: await generateTypeFromSchema( | ||
schemaDocument, | ||
extension.codegenConfig | ||
), | ||
extension, | ||
}) | ||
) | ||
.catch(errorCatcher); | ||
}, | ||
checkValidity: async (currentItem) => { | ||
const isGraphQLConfigValid = | ||
await cachedGraphQLConfigLoader.checkItemValidity(null); | ||
if (!isGraphQLConfigValid) { | ||
return false; | ||
} | ||
|
||
const project = await currentItem.value; | ||
if (!project) { | ||
return true; | ||
} | ||
|
||
if (!project.schemaFilePath) { | ||
return false; | ||
} | ||
|
||
return checkFileLastUpdate(project.schemaFilePath, currentItem.dateTime); | ||
}, | ||
sizeLimit: 40, | ||
}); |
7 changes: 5 additions & 2 deletions
7
src/source-update/generate-bottom-content.ts → src/generators/generate-bottom-content.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
710e298
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"with ts-gql-plugin" vs "without ts-gql-plugin" Benchmark
performance impact %: "with ts-gql-plugin" vs "without ts-gql-plugin"
18.82
% (±5.57%
)19.39
% (±7.66%
)1.03
This comment was automatically generated by workflow using github-action-benchmark.