diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a61bb617ced..11516ed033b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -110,6 +110,9 @@ jobs: timeout_minutes: 10 max_attempts: 5 command: yarn test:leaks --ci + - name: Tests using Node.js runner + if: ${{ matrix.node-version >= 22 }} + run: yarn test:node trackback: name: trackback rc dependencies @@ -158,7 +161,7 @@ jobs: with: timeout_minutes: 10 max_attempts: 5 - command: TEST_BROWSER=true yarn jest --no-watchman --ci browser + command: TEST_BROWSER=true yarn jest --no-watchman --ci browser analyze: name: Analyze runs-on: ubuntu-latest diff --git a/package.json b/package.json index 3a9c5a0e652..7507018ae3e 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "test": "jest --no-watchman", "test-fed-compat": "ts-node --transpileOnly --compiler-options='{\"module\":\"commonjs\"}' scripts/fetch-federation-tests && yarn test federation-compat", "test:leaks": "cross-env \"LEAK_TEST=1\" jest --no-watchman --detectOpenHandles --detectLeaks --logHeapUsage", + "test:node": "node --experimental-strip-types --test **/tests/**/*.node.*s", "ts:check": "tsc --noEmit" }, "devDependencies": { diff --git a/packages/load/src/utils/custom-loader.ts b/packages/load/src/utils/custom-loader.ts index 79f5c9435d9..984b2de60ea 100644 --- a/packages/load/src/utils/custom-loader.ts +++ b/packages/load/src/utils/custom-loader.ts @@ -1,20 +1,32 @@ import { createRequire } from 'module'; -import { join as joinPaths } from 'path'; +import { isAbsolute, join as joinPaths } from 'path'; -export function getCustomLoaderByPath(path: string, cwd: string) { +function extractLoaderFromModule(loaderModule: any) { + if (loaderModule) { + if (loaderModule.default && typeof loaderModule.default === 'function') { + return loaderModule.default; + } + if (typeof loaderModule === 'function') { + return loaderModule; + } + } +} + +export async function getCustomLoaderByPath(path: string, cwd: string) { try { - const requireFn = createRequire(joinPaths(cwd, 'noop.js')); - const requiredModule = requireFn(path); + const absolutePath = isAbsolute(path) ? path : joinPaths(cwd, path); + const importedModule = await import(absolutePath); + return extractLoaderFromModule(importedModule); + } catch (e: any) {} - if (requiredModule) { - if (requiredModule.default && typeof requiredModule.default === 'function') { - return requiredModule.default; - } + return null; +} - if (typeof requiredModule === 'function') { - return requiredModule; - } - } +function getCustomLoaderByPathSync(path: string, cwd: string) { + try { + const requireFn = createRequire(joinPaths(cwd, 'noop.js')); + const requiredModule = requireFn(path); + return extractLoaderFromModule(requiredModule); } catch (e: any) {} return null; @@ -40,7 +52,7 @@ export function useCustomLoaderSync(loaderPointer: any, cwd: string) { let loader; if (typeof loaderPointer === 'string') { - loader = getCustomLoaderByPath(loaderPointer, cwd); + loader = getCustomLoaderByPathSync(loaderPointer, cwd); } else if (typeof loaderPointer === 'function') { loader = loaderPointer; } diff --git a/packages/load/tests/custom-loader.js b/packages/load/tests/custom-loader.cjs similarity index 100% rename from packages/load/tests/custom-loader.js rename to packages/load/tests/custom-loader.cjs diff --git a/packages/load/tests/custom-loader.mjs b/packages/load/tests/custom-loader.mjs new file mode 100644 index 00000000000..eb0a169c24b --- /dev/null +++ b/packages/load/tests/custom-loader.mjs @@ -0,0 +1,20 @@ +import { buildSchema, parse } from 'graphql'; + +const loader = function (_, { customLoaderContext: { loaderType }, fooFieldName }) { + if (loaderType === 'documents') { + return parse(/* GraphQL */ ` + query TestQuery { + ${fooFieldName} + } + `); + } else if (loaderType === 'schema') { + return buildSchema(/* GraphQL */ ` + type Query { + ${fooFieldName}: String + } + `); + } + return 'I like turtles'; +}; + +export default loader; diff --git a/packages/load/tests/loaders/documents/documents-from-glob.spec.ts b/packages/load/tests/loaders/documents/documents-from-glob.spec.ts index 10696a1e88e..601fbde66bd 100644 --- a/packages/load/tests/loaders/documents/documents-from-glob.spec.ts +++ b/packages/load/tests/loaders/documents/documents-from-glob.spec.ts @@ -171,7 +171,7 @@ describe('documentsFromGlob', () => { loaderType: 'documents', }; const pointerOptions = { - loader: join(__dirname, '../../custom-loader.js'), + loader: join(__dirname, '../../custom-loader.cjs'), fooFieldName: 'myFooField', }; const result = await load( diff --git a/packages/load/tests/loaders/schema/integration.spec.ts b/packages/load/tests/loaders/schema/integration.spec.ts index a531a23f10d..e4d1380609d 100644 --- a/packages/load/tests/loaders/schema/integration.spec.ts +++ b/packages/load/tests/loaders/schema/integration.spec.ts @@ -150,7 +150,7 @@ describe('loadSchema', () => { loaderType: 'schema', }; const pointerOptions = { - loader: join(__dirname, '../../custom-loader.js'), + loader: join(__dirname, '../../custom-loader.cjs'), fooFieldName: 'myFooField', }; const result = await load( diff --git a/packages/load/tests/use-custom-loader.node.mjs b/packages/load/tests/use-custom-loader.node.mjs new file mode 100644 index 00000000000..2de785ee009 --- /dev/null +++ b/packages/load/tests/use-custom-loader.node.mjs @@ -0,0 +1,26 @@ +import assert from 'node:assert'; +import { describe, it } from 'node:test'; +import { versionInfo as graphqlVersionInfo } from 'graphql'; +import { useCustomLoader, useCustomLoaderSync } from '../src/utils/custom-loader.ts'; + +describe('useCustomLoader', () => { + const skipCjsTests = graphqlVersionInfo.major >= 17; + + it('can load a custom cjs loader from a file path', { skip: skipCjsTests }, async () => { + const loader = await useCustomLoader(`./custom-loader.cjs`, import.meta.dirname); + const result = await loader('some-name', { customLoaderContext: {} }); + assert.strictEqual(result, 'I like turtles'); + }); + + it('can load a custom cjs loader synchronously', { skip: skipCjsTests }, async () => { + const loader = useCustomLoaderSync(`./custom-loader.cjs`, import.meta.dirname); + const result = await loader('some-name', { customLoaderContext: {} }); + assert.strictEqual(result, 'I like turtles'); + }); + + it('can load a custom mjs loader from a file path', async () => { + const loader = await useCustomLoader(`./custom-loader.mjs`, import.meta.dirname); + const result = await loader('some-name', { customLoaderContext: {} }); + assert.strictEqual(result, 'I like turtles'); + }); +}); diff --git a/packages/load/tests/use-custom-loader.spec.ts b/packages/load/tests/use-custom-loader.spec.ts index 7fd902f0647..9da3c91f91a 100644 --- a/packages/load/tests/use-custom-loader.spec.ts +++ b/packages/load/tests/use-custom-loader.spec.ts @@ -2,7 +2,7 @@ import { getCustomLoaderByPath } from '../src/utils/custom-loader.js'; describe('getCustomLoaderByPath', () => { it('can load a custom loader from a file path', async () => { - const loader = getCustomLoaderByPath('./custom-loader.js', __dirname); + const loader = await getCustomLoaderByPath('./custom-loader.cjs', __dirname); expect(loader).toBeDefined(); expect(loader('some-name', { customLoaderContext: {} })).toEqual('I like turtles'); });