Skip to content

Commit f51a619

Browse files
committed
Performance tweaks and cleanup
1 parent a2bb768 commit f51a619

File tree

7 files changed

+789
-93
lines changed

7 files changed

+789
-93
lines changed

.gitignore

Lines changed: 57 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,87 @@
1+
# Dependencies
2+
node_modules/
3+
jspm_packages/
4+
5+
# Build outputs
6+
dist/
7+
build/
8+
*.tgz
9+
110
# Logs
2-
logs
11+
logs/
312
*.log
413
npm-debug.log*
514
yarn-debug.log*
615
yarn-error.log*
16+
lerna-debug.log*
717

818
# Runtime data
9-
pids
19+
pids/
1020
*.pid
1121
*.seed
1222
*.pid.lock
1323

14-
# Directory for instrumented libs generated by jscoverage/JSCover
15-
lib-cov
16-
17-
# Coverage directory used by tools like istanbul
18-
coverage
19-
20-
# nyc test coverage
21-
.nyc_output
22-
23-
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24-
.grunt
25-
26-
# Bower dependency directory (https://bower.io/)
27-
bower_components
28-
29-
# node-waf configuration
30-
.lock-wscript
31-
32-
# Compiled binary addons (https://nodejs.org/api/addons.html)
33-
build/Release
24+
# Coverage and testing
25+
coverage/
26+
.nyc_output/
27+
lib-cov/
3428

35-
# Dependency directories
36-
node_modules/
37-
jspm_packages/
38-
39-
# Typescript v1 declaration files
40-
typings/
41-
42-
# Optional npm cache directory
43-
.npm
29+
# Environment variables
30+
.env
31+
.env.local
32+
.env.*.local
4433

45-
# Optional eslint cache
34+
# Caching
35+
.npm/
4636
.eslintcache
37+
.yarn-integrity
4738

4839
# Optional REPL history
4940
.node_repl_history
5041

51-
# Output of 'npm pack'
52-
*.tgz
53-
54-
# Yarn Integrity file
55-
.yarn-integrity
56-
57-
# dotenv environment variables file
58-
.env
59-
60-
# next.js build output
61-
.next
42+
# Package manager files (consistent with .npmrc setting)
43+
package-lock.json
44+
yarn.lock
45+
pnpm-lock.yaml
6246

63-
# Project files
64-
.project
65-
.idea
66-
.settings
67-
.iml
68-
*.iml
69-
*.sublime-workspace
70-
*.sublime-project
47+
# IDE and editor files
7148
.vscode/*
7249
!.vscode/settings.json
7350
!.vscode/tasks.json
7451
!.vscode/launch.json
7552
!.vscode/extensions.json
53+
.idea/
54+
*.iml
55+
*.sublime-workspace
56+
*.sublime-project
57+
.project
58+
.settings/
7659

77-
# Misc
78-
.DS_Store*
79-
ehthumbs.db
80-
Icon?
81-
Thumbs.db
82-
.AppleDouble
83-
.LSOverride
60+
# AI coding assistants
61+
.cursor/
62+
.copilot/
63+
64+
# OS generated files
65+
.DS_Store
66+
.DS_Store?
67+
._*
8468
.Spotlight-V100
8569
.Trashes
70+
ehthumbs.db
71+
Thumbs.db
72+
Desktop.ini
73+
74+
# Temporary files
75+
*.tmp
76+
*.temp
8677
*.swp
78+
*.swo
79+
*~
8780

88-
# Package lock
89-
package-lock.json
81+
# Next.js (if ever used in examples)
82+
.next/
83+
out/
9084

91-
# Build output
92-
dist/
85+
# Misc
86+
.AppleDouble
87+
.LSOverride

.npmignore

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,81 @@
1+
# Source files (only publish compiled dist/)
2+
src/
3+
*.ts
4+
!*.d.ts
5+
6+
# Development and testing
17
test/
8+
tests/
9+
__tests__/
210
examples/
3-
.vscode
411
coverage/
512
.nyc_output/
6-
azure-pipelines.yml
7-
.eslintrc
8-
.github
9-
.nycrc
13+
14+
# Configuration files
15+
tsconfig.json
16+
eslint.config.mjs
17+
.eslintrc*
18+
.prettier*
19+
jest.config.*
20+
.babelrc*
21+
webpack.config.*
22+
rollup.config.*
23+
24+
# CI/CD and deployment
25+
.github/
26+
.gitlab-ci.yml
27+
azure-pipelines.yml
28+
.travis.yml
29+
.circleci/
30+
Dockerfile
1031
.dockerignore
11-
__tests__.js
32+
.deployment/
33+
34+
# IDE and editor files
35+
.vscode/
36+
.idea/
37+
*.iml
38+
*.sublime-workspace
39+
*.sublime-project
40+
41+
# AI coding assistants
42+
.cursor/
43+
.copilot/
44+
45+
# Package manager files
46+
.npmrc
47+
yarn.lock
48+
pnpm-lock.yaml
49+
package-lock.json
50+
51+
# Environment and secrets
52+
.env*
53+
.secrets
54+
55+
# Documentation development
56+
docs/
57+
*.md
58+
!README.md
59+
60+
# Logs and temporary files
61+
logs/
62+
*.log
63+
*.tmp
64+
*.temp
65+
.DS_Store
66+
Thumbs.db
67+
68+
# Development scripts
69+
scripts/dev/
70+
scripts/build/
71+
scripts/test/
72+
73+
# Linting and formatting
74+
.eslintcache
75+
76+
# Miscellaneous
77+
.gitignore
78+
.gitattributes
79+
.editorconfig
80+
.commitlintrc*
81+
.husky/

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1+
### v6.0.2
2+
3+
- [PERFORMANCE] Enhanced context building with parallel import processing, and middleware optimization
4+
- [TESTS] Added comprehensive performance regression tests to validate optimization correctness and prevent breaking changes
5+
6+
### v6.0.1
7+
8+
- Re-release after merge
9+
110
### v6.0.0
211

312
- Converted to TS
413
- BREAKING removed `delegateToComponent`
514
- BREAKING excludes removed as an option in import configuration. Transforms used instead as part of a `SubschemaConfig`.
615
- BREAKING upgraded to graphql 16.9+ peer
16+
- BREAKING datasource injected context does not contain `datasources`
717

818
===
919

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "graphql-component",
3-
"version": "6.0.1",
3+
"version": "6.0.2",
44
"description": "Build, customize and compose GraphQL schemas in a componentized fashion",
55
"keywords": [
66
"graphql",

src/index.ts

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ export default class GraphQLComponent<TContextType extends ComponentContext = Co
129129
_dataSourceContextInject: DataSourceInjectionFunction;
130130
_transforms: SchemaMapper[]
131131
private _transformedSchema: GraphQLSchema;
132+
private _middleware: MiddlewareEntry[] = [];
132133

133134
constructor({
134135
types,
@@ -180,27 +181,39 @@ export default class GraphQLComponent<TContextType extends ComponentContext = Co
180181
}
181182
}) : [];
182183

183-
184184
this._context = async (globalContext: Record<string, unknown>): Promise<TContextType> => {
185-
//TODO: currently the context injected into data sources won't have data sources on it
185+
//BREAKING: The context injected into data sources won't have data sources on it
186186
const ctx = {
187187
dataSources: this._dataSourceContextInject(globalContext)
188188
};
189189

190-
for (const { component } of this.imports) {
191-
const { dataSources, ...importedContext } = await component.context(globalContext);
192-
Object.assign(ctx.dataSources, dataSources);
193-
Object.assign(ctx, importedContext);
190+
// Only process imports if they exist
191+
if (this._imports.length > 0) {
192+
// Process imports in parallel if they're independent
193+
const importPromises = this._imports.map(async ({ component }) => {
194+
const importContext = await component.context(globalContext);
195+
return importContext;
196+
});
197+
198+
const importResults = await Promise.all(importPromises);
199+
200+
// Merge results efficiently
201+
for (const { dataSources, ...importedContext } of importResults) {
202+
Object.assign(ctx.dataSources, dataSources);
203+
Object.assign(ctx, importedContext);
204+
}
194205
}
195206

207+
// Handle namespace context if present
196208
if (context) {
197209
debug(`building ${context.namespace} context`);
198210

199211
if (!ctx[context.namespace]) {
200212
ctx[context.namespace] = {};
201213
}
202214

203-
Object.assign(ctx[context.namespace], await context.factory.call(this, globalContext));
215+
const namespaceContext = await context.factory.call(this, globalContext);
216+
Object.assign(ctx[context.namespace], namespaceContext);
204217
}
205218

206219
return ctx as TContextType;
@@ -211,43 +224,42 @@ export default class GraphQLComponent<TContextType extends ComponentContext = Co
211224
}
212225

213226
get context(): IContextWrapper {
214-
227+
// Cache middleware array to avoid recreation
215228
const contextFn = async (context: Record<string, unknown>): Promise<ComponentContext> => {
216229
debug(`building root context`);
217230

218-
const middleware: MiddlewareEntry[] = (contextFn as any)._middleware || [];
231+
let processedContext = context;
219232

220-
for (const { name, fn } of middleware) {
221-
debug(`applying ${name} middleware`);
222-
context = await fn(context);
233+
// Apply middleware more efficiently
234+
if (this._middleware.length > 0) {
235+
for (const { name, fn } of this._middleware) {
236+
debug(`applying ${name} middleware`);
237+
processedContext = await fn(processedContext);
238+
}
223239
}
224240

225-
const componentContext = await this._context(context);
226-
227-
const globalContext = {
228-
...context,
229-
...componentContext
230-
};
241+
const componentContext = await this._context(processedContext);
231242

232-
return globalContext;
243+
// More efficient object composition
244+
return Object.assign({}, processedContext, componentContext);
233245
};
234246

235-
contextFn._middleware = [];
236-
237-
contextFn.use = function (name: string, fn: ContextFunction): IContextWrapper {
247+
contextFn.use = (name: string | ContextFunction, fn?: ContextFunction): IContextWrapper => {
238248
if (typeof name === 'function') {
239249
fn = name;
240250
name = 'unknown';
241251
}
242252
debug(`adding ${name} middleware`);
243-
contextFn._middleware.push({ name, fn });
253+
this._middleware.push({ name: name as string, fn: fn! });
244254

245255
return contextFn;
246256
};
247257

248258
return contextFn;
249259
}
250260

261+
262+
251263
get name(): string {
252264
return this.constructor.name;
253265
}
@@ -374,8 +386,8 @@ export default class GraphQLComponent<TContextType extends ComponentContext = Co
374386
for (const [key, fn] of Object.entries(transform)) {
375387
if (!mapping[key]) {
376388
functions[key] = [];
377-
let result = undefined;
378389
mapping[key] = function (...args) {
390+
let result;
379391
while (functions[key].length) {
380392
const mapper = functions[key].shift();
381393
result = mapper(...args);

0 commit comments

Comments
 (0)