Skip to content

Commit 30cb4e7

Browse files
committed
Publish as ESM-only
Require Node v20.19 (which enables require(esm) support by default). This new package is supposed to be compatible with Apollo Server 4 which does an elaborate dance to publish as both CJS and ESM, but we already decided it was OK for this package itself to require Node v20. Turns out CJS-only plays poorly with the AS4-style dual build: apollographql/apollo-server#8069 And we're likely to go ESM-only with AS5 because newer Node makes this easier. So let's do that here too. Making tests work requires: - Tweaking ts-jest to some new presets. - Passing an experimental option to Node (and disabling the warning about it). - Upgrading the integration test suite to a version that is dual-built instead of CJS-only (otherwise we end up with different ApolloServers from the `@apollo/server` we load directly (ESM) and via the integration test suite (CJS)). - I had a lot of trouble getting imports in the tests to work. It ended up working if I import from `dist` and are sure to build first, which seems wrong. But it works.
1 parent bfa8775 commit 30cb4e7

File tree

8 files changed

+98
-94
lines changed

8 files changed

+98
-94
lines changed

.changeset/modern-kiwis-look.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@as-integrations/express5': major
3+
---
4+
5+
Publish as ESM-only, and require Node v20.19 (which enables require(esm) support by default).

jest.config.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
import type { Config } from '@jest/types';
1+
import type { Config } from 'jest';
2+
import { createDefaultEsmPreset } from 'ts-jest';
23

3-
const config: Config.InitialOptions = {
4-
preset: 'ts-jest',
5-
testEnvironment: 'node',
6-
roots: ['src'],
7-
transform: {
8-
'/__tests__/.*.test.ts$': ['ts-jest', { tsconfig: 'tsconfig.test.json' }],
9-
},
10-
testRegex: '/__tests__/.*.test.ts$',
11-
verbose: true,
12-
};
4+
const presetConfig = createDefaultEsmPreset({
5+
tsconfig: 'tsconfig.test.json',
6+
});
137

14-
export default config;
8+
export default {
9+
...presetConfig,
10+
testRegex: '/__tests__/.*.test.ts$',
11+
} satisfies Config;

package-lock.json

Lines changed: 14 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
"bugs": {
1313
"url": "https://github.com/apollo-server-integrations/apollo-server-integration-express5/issues"
1414
},
15-
"main": "dist/index.js",
16-
"types": "dist/index.d.ts",
15+
"type": "module",
16+
"exports": {
17+
".": "./dist/index.js"
18+
},
1719
"engines": {
18-
"node": ">=20"
20+
"node": ">=20.19"
1921
},
2022
"scripts": {
2123
"build": "tsc --build tsconfig.build.json",
@@ -27,14 +29,16 @@
2729
"changeset-check": "changeset status --verbose --since=origin/main",
2830
"changeset-version": "changeset version && npm i",
2931
"spell-check": "cspell lint '**' --no-progress || (echo 'Add any real words to cspell-dict.txt.'; exit 1)",
30-
"test": "jest",
31-
"test:ci": "jest --coverage --ci --maxWorkers=2 --reporters=default --reporters=jest-junit",
32+
"pretest": "npm run build",
33+
"test": "NODE_OPTIONS=\"--experimental-vm-modules --disable-warning=ExperimentalWarning\" jest",
34+
"test:ci": "npm run test --coverage --ci --maxWorkers=2 --reporters=default --reporters=jest-junit",
3235
"watch": "tsc --build --watch",
36+
"prelint": "npm run build",
3337
"lint": "eslint ."
3438
},
3539
"devDependencies": {
36-
"@apollo/server": "4.12.0",
37-
"@apollo/server-integration-testsuite": "4.12.0",
40+
"@apollo/server": "4.12.2",
41+
"@apollo/server-integration-testsuite": "4.12.2",
3842
"@apollo/utils.withrequired": "3.0.0",
3943
"@changesets/changelog-github": "0.5.1",
4044
"@changesets/cli": "2.29.3",

src/__tests__/expressSpecific.test.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ import request from 'supertest';
33
import compression, { filter as defaultFilter } from 'compression';
44
import type { BaseContext } from '@apollo/server';
55
import { ApolloServer } from '@apollo/server';
6-
import { expressMiddleware } from '..';
6+
import { expressMiddleware } from '../../dist/index.js';
77
import { it, expect } from '@jest/globals';
88
import cors from 'cors';
9-
import { resolvable } from './utils';
109

1110
it('gives helpful error if json middleware is not installed', async () => {
1211
const server = new ApolloServer({ typeDefs: 'type Query {f: ID}' });
@@ -203,3 +202,36 @@ it('supporting doubly-encoded variables example from migration guide', async ()
203202

204203
await server.stop();
205204
});
205+
206+
// Copyright 2019 Joseph Gentle
207+
208+
// Permission to use, copy, modify, and / or distribute this software for any
209+
// purpose with or without fee is hereby granted, provided that the above
210+
// copyright notice and this permission notice appear in all copies.
211+
212+
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
213+
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
214+
// FITNESS.IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
215+
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
216+
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
217+
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
218+
// PERFORMANCE OF THIS SOFTWARE.
219+
220+
export type Resolvable<T> = Promise<T> & {
221+
resolve: (t: T) => void;
222+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
223+
reject: (e: any) => void;
224+
};
225+
226+
export function resolvable<T = void>(): Resolvable<T> {
227+
let resolve: (val: T) => void;
228+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
229+
let reject: (err: any) => void;
230+
const promise = new Promise<T>((_resolve, _reject) => {
231+
resolve = _resolve;
232+
reject = _reject;
233+
}) as Resolvable<T>;
234+
promise.resolve = resolve!;
235+
promise.reject = reject!;
236+
return promise;
237+
}

src/__tests__/integration.test.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,28 @@ import cors from 'cors';
1010
import express, { json } from 'express';
1111
import type { RequestListener } from 'http';
1212
import http from 'http';
13-
import { expressMiddleware } from '..';
14-
import { urlForHttpServer } from './utils';
13+
import { expressMiddleware } from '../../dist/index.js';
14+
import type { Server } from 'http';
15+
import type { AddressInfo } from 'net';
16+
import { format } from 'url';
17+
18+
export function urlForHttpServer(httpServer: Server): string {
19+
const { address, port } = httpServer.address() as AddressInfo;
20+
21+
// Convert IPs which mean "any address" (IPv4 or IPv6) into localhost
22+
// corresponding loopback ip. Note that the url field we're setting is
23+
// primarily for consumption by our test suite. If this heuristic is wrong for
24+
// your use case, explicitly specify a frontend host (in the `host` option
25+
// when listening).
26+
const hostname = address === '' || address === '::' ? 'localhost' : address;
27+
28+
return format({
29+
protocol: 'http',
30+
hostname,
31+
port,
32+
pathname: '/',
33+
});
34+
}
1535

1636
defineIntegrationTestSuite(async function (
1737
serverOptions: ApolloServerOptions<BaseContext>,

src/__tests__/utils.ts

Lines changed: 0 additions & 54 deletions
This file was deleted.

tsconfig.base.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
"compilerOptions": {
33
"rootDir": "./src",
44
"outDir": "./dist",
5-
"target": "es2020",
6-
"module": "commonjs",
7-
"moduleResolution": "node",
5+
"target": "ESNext",
6+
"module": "NodeNext",
87
"esModuleInterop": true,
8+
"isolatedModules": true,
99
"sourceMap": true,
1010
"declaration": true,
1111
"declarationMap": true,

0 commit comments

Comments
 (0)