Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(include,serve-cli,compose-cli): Optionally use native import for importing #7494

Merged
merged 5 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/thirty-pumas-talk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@graphql-mesh/include': minor
---

Optional `nativeImport` argument when including to use the native `import` function instead of jiti
7 changes: 7 additions & 0 deletions .changeset/warm-lizards-impress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@graphql-mesh/compose-cli': minor
'@graphql-mesh/serve-cli': minor
---

Support for `--native-import` flag which will use the native `import` function instead of jiti for
importing configs
89 changes: 89 additions & 0 deletions e2e/top-level-await/__snapshots__/top-level-await.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should compose 1`] = `
"
schema
@link(url: "https://specs.apollo.dev/link/v1.0")
@link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION)






{
query: Query


}


directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE

directive @join__field(
graph: join__Graph
requires: join__FieldSet
provides: join__FieldSet
type: String
external: Boolean
override: String
usedOverridden: Boolean
) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION

directive @join__graph(name: String!, url: String!) on ENUM_VALUE

directive @join__implements(
graph: join__Graph!
interface: String!
) repeatable on OBJECT | INTERFACE

directive @join__type(
graph: join__Graph!
key: join__FieldSet
extension: Boolean! = false
resolvable: Boolean! = true
isInterfaceObject: Boolean! = false
) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR

directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION

scalar join__FieldSet


directive @link(
url: String
as: String
for: link__Purpose
import: [link__Import]
) repeatable on SCHEMA

scalar link__Import

enum link__Purpose {
"""
\`SECURITY\` features provide metadata necessary to securely resolve fields.
"""
SECURITY

"""
\`EXECUTION\` features provide metadata necessary for operation execution.
"""
EXECUTION
}







enum join__Graph {
HELLOWORLD @join__graph(name: "helloworld", url: "")
}

type Query @join__type(graph: HELLOWORLD) {
hello: String
}

"
`;
29 changes: 29 additions & 0 deletions e2e/top-level-await/mesh.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { GraphQLObjectType, GraphQLSchema, GraphQLString } from 'graphql';
import { defineConfig as defineComposeConfig } from '@graphql-mesh/compose-cli';
import { defineConfig as defineServeConfig } from '@graphql-mesh/serve-cli';

// top level await
await Promise.resolve();

export const composeConfig = defineComposeConfig({
subgraphs: [
{
sourceHandler: () => ({
name: 'helloworld',
schema$: new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
hello: {
type: GraphQLString,
resolve: () => 'world',
},
},
}),
}),
}),
},
],
});

export const serveConfig = defineServeConfig({});
10 changes: 10 additions & 0 deletions e2e/top-level-await/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "@e2e/top-level-await",
"type": "module",
"private": true,
"dependencies": {
"@graphql-mesh/compose-cli": "workspace:*",
"@graphql-mesh/serve-cli": "workspace:*",
"graphql": "16.9.0"
}
}
20 changes: 20 additions & 0 deletions e2e/top-level-await/top-level-await.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { createTenv } from '@e2e/tenv';
import { fetch } from '@whatwg-node/fetch';

const { serve, compose, fs } = createTenv(__dirname);

it('should serve', async () => {
const proc = await serve({
supergraph: await fs.tempfile('supergraph.graphql', 'type Query { hello: String }'),
args: ['--native-import'],
});
const res = await fetch(`http://0.0.0.0:${proc.port}/healthcheck`);
expect(res.ok).toBeTruthy();
});

it('should compose', async () => {
const proc = await compose({
args: ['--native-import'],
});
expect(proc.result).toMatchSnapshot();
});
20 changes: 14 additions & 6 deletions e2e/utils/tenv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ export interface ProcOptions {
* They will be merged with `process.env` overriding any existing value.
*/
env?: Record<string, string | number>;
/** Extra args to pass to the process. */
args?: (string | number | boolean)[];
}

export interface Proc extends AsyncDisposable {
Expand Down Expand Up @@ -243,9 +245,9 @@ export function createTenv(cwd: string): Tenv {
return fs.writeFile(filePath, content, 'utf-8');
},
},
spawn(command, opts) {
spawn(command, { args: extraArgs = [], ...opts } = {}) {
const [cmd, ...args] = Array.isArray(command) ? command : command.split(' ');
return spawn({ ...opts, cwd }, String(cmd), ...args);
return spawn({ ...opts, cwd }, String(cmd), ...args, ...extraArgs);
},
serveRunner,
async serve(opts) {
Expand All @@ -255,6 +257,7 @@ export function createTenv(cwd: string): Tenv {
pipeLogs = boolEnv('DEBUG'),
env,
runner,
args = [],
} = opts || {};

let proc: Proc,
Expand Down Expand Up @@ -314,7 +317,7 @@ export function createTenv(cwd: string): Tenv {
hostPort: port,
containerPort: port,
healthcheck: ['CMD-SHELL', `wget --spider http://0.0.0.0:${port}/healthcheck`],
cmd: [createPortOpt(port), ...(supergraph ? ['supergraph', supergraph] : [])],
cmd: [createPortOpt(port), ...(supergraph ? ['supergraph', supergraph] : []), ...args],
volumes,
pipeLogs,
});
Expand All @@ -329,6 +332,7 @@ export function createTenv(cwd: string): Tenv {
'supergraph',
supergraph,
createPortOpt(port),
...args,
);
}

Expand Down Expand Up @@ -374,6 +378,7 @@ export function createTenv(cwd: string): Tenv {
maskServicePorts,
pipeLogs = boolEnv('DEBUG'),
env,
args = [],
} = opts || {};
let output = '';
if (opts?.output) {
Expand All @@ -389,6 +394,7 @@ export function createTenv(cwd: string): Tenv {
path.resolve(__project, 'packages', 'compose-cli', 'src', 'bin.ts'),
output && createOpt('output', output),
...services.map(({ name, port }) => createServicePortOpt(name, port)),
...args,
);
await waitForExit;
let result = '';
Expand Down Expand Up @@ -423,7 +429,7 @@ export function createTenv(cwd: string): Tenv {

return { ...proc, output, result };
},
async service(name, { port, servePort, pipeLogs = boolEnv('DEBUG') } = {}) {
async service(name, { port, servePort, pipeLogs = boolEnv('DEBUG'), args = [] } = {}) {
port ||= await getAvailablePort();
const [proc, waitForExit] = await spawn(
{ cwd, pipeLogs },
Expand All @@ -433,6 +439,7 @@ export function createTenv(cwd: string): Tenv {
path.join(cwd, 'services', name),
createServicePortOpt(name, port),
servePort && createPortOpt(servePort),
...args,
);
const service: Service = { ...proc, name, port };
const ctrl = new AbortController();
Expand All @@ -458,8 +465,9 @@ export function createTenv(cwd: string): Tenv {
additionalContainerPorts: containerAdditionalPorts,
healthcheck,
pipeLogs = boolEnv('DEBUG'),
cmd,
cmd = [],
volumes = [],
args = [],
}) {
const containerName = `${name}_${Math.random().toString(32).slice(2)}`;

Expand Down Expand Up @@ -528,7 +536,7 @@ export function createTenv(cwd: string): Tenv {
{},
),
},
Cmd: cmd?.filter(Boolean).map(String),
Cmd: [...cmd, ...args].filter(Boolean).map(String),
HostConfig: {
AutoRemove: true,
PortBindings: {
Expand Down
7 changes: 4 additions & 3 deletions packages/compose-cli/src/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ let program = new Command()
).env('CONFIG_PATH'),
)
.option('--subgraph <name>', 'name of the subgraph to compose')
.option('-o, --output <path>', 'path to the output file');
.option('-o, --output <path>', 'path to the output file')
.option('--native-import', 'use the native "import" function for importing the config file');

export interface RunOptions extends ReturnType<typeof program.opts> {
/** @default new DefaultLogger() */
Expand Down Expand Up @@ -72,7 +73,7 @@ export async function run({
.catch(() => false);
if (exists) {
log.info(`Found default config file ${configPath}`);
const module = await include(absoluteConfigPath);
const module = await include(absoluteConfigPath, opts.nativeImport);
importedConfig = Object(module).composeConfig;
if (!importedConfig) {
throw new Error(`No "composeConfig" exported from default config at ${configPath}`);
Expand All @@ -98,7 +99,7 @@ export async function run({
if (!exists) {
throw new Error(`Cannot find config file at ${configPath}`);
}
const module = await include(configPath);
const module = await include(configPath, opts.nativeImport);
importedConfig = Object(module).composeConfig;
if (!importedConfig) {
throw new Error(`No "composeConfig" exported from config at ${configPath}`);
Expand Down
4 changes: 2 additions & 2 deletions packages/include/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ const jiti = createJITI(
*
* If the module at {@link path} is not found, `null` will be returned.
*/
export async function include<T = any>(path: string): Promise<T> {
const module = await jiti.import(path, {});
export async function include<T = any>(path: string, nativeImport?: boolean): Promise<T> {
const module = await (nativeImport ? import(path) : jiti.import(path, {}));
if (!module) {
throw new Error('Included module is empty');
}
Expand Down
1 change: 1 addition & 0 deletions packages/serve-cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ let cli = new Command()
// see here https://github.com/tj/commander.js/blob/970ecae402b253de691e6a9066fea22f38fe7431/lib/command.js#L655
null,
)
.option('--native-import', 'use the native "import" function for importing the config file')
.addOption(
new Option(
'--hive-registry-token <token>',
Expand Down
3 changes: 2 additions & 1 deletion packages/serve-cli/src/commands/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ export const addCommand: AddCommand = (ctx, cli) =>
).env('HIVE_CDN_KEY'),
)
.action(async function proxy(endpoint) {
const { hiveCdnKey, hiveRegistryToken, maskedErrors, polling, ...opts } =
const { hiveCdnKey, hiveRegistryToken, maskedErrors, polling, nativeImport, ...opts } =
this.optsWithGlobals<CLIGlobals>();
const loadedConfig = await loadConfig({
log: ctx.log,
configPath: opts.configPath,
quiet: !cluster.isPrimary,
nativeImport,
});

let proxy: MeshServeConfigProxy['proxy'] | undefined;
Expand Down
3 changes: 2 additions & 1 deletion packages/serve-cli/src/commands/subgraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ export const addCommand: AddCommand = (ctx, cli) =>
'path to the subgraph schema file or a url from where to pull the subgraph schema (default: "subgraph.graphql")',
)
.action(async function subgraph(schemaPathOrUrl) {
const { maskedErrors, hiveRegistryToken, polling, ...opts } =
const { maskedErrors, hiveRegistryToken, polling, nativeImport, ...opts } =
this.optsWithGlobals<CLIGlobals>();
const loadedConfig = await loadConfig({
log: ctx.log,
configPath: opts.configPath,
quiet: !cluster.isPrimary,
nativeImport,
});

let subgraph: UnifiedGraphConfig = 'subgraph.graphql';
Expand Down
3 changes: 2 additions & 1 deletion packages/serve-cli/src/commands/supergraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ export const addCommand: AddCommand = (ctx, cli) =>
).env('HIVE_CDN_KEY'),
)
.action(async function supergraph(schemaPathOrUrl) {
const { hiveCdnKey, hiveRegistryToken, maskedErrors, polling, ...opts } =
const { hiveCdnKey, hiveRegistryToken, maskedErrors, polling, nativeImport, ...opts } =
this.optsWithGlobals<CLIGlobals>();
const loadedConfig = await loadConfig({
log: ctx.log,
configPath: opts.configPath,
quiet: !cluster.isPrimary,
nativeImport,
});

let supergraph: UnifiedGraphConfig | MeshServeHiveCDNOptions = 'supergraph.graphql';
Expand Down
5 changes: 3 additions & 2 deletions packages/serve-cli/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export async function loadConfig<TContext extends Record<string, any> = Record<s
quiet?: boolean;
log: Logger;
configPath: string | null | undefined;
nativeImport: boolean | undefined;
}) {
let importedConfig: Partial<MeshServeConfig<TContext> & ServerConfig> | null = null;

Expand All @@ -32,7 +33,7 @@ export async function loadConfig<TContext extends Record<string, any> = Record<s
.catch(() => false);
if (exists) {
!opts.quiet && opts.log.info(`Found default config file ${configPath}`);
const module = await include(absoluteConfigPath);
const module = await include(absoluteConfigPath, opts.nativeImport);
importedConfig = Object(module).serveConfig || null;
if (!importedConfig) {
!opts.quiet &&
Expand All @@ -53,7 +54,7 @@ export async function loadConfig<TContext extends Record<string, any> = Record<s
if (!exists) {
throw new Error(`Cannot find config file at ${configPath}`);
}
const module = await include(configPath);
const module = await include(configPath, opts.nativeImport);
importedConfig = Object(module).serveConfig || null;
if (!importedConfig) {
throw new Error(`No "serveConfig" exported from config file at ${configPath}`);
Expand Down
10 changes: 10 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3875,6 +3875,16 @@ __metadata:
languageName: unknown
linkType: soft

"@e2e/top-level-await@workspace:e2e/top-level-await":
version: 0.0.0-use.local
resolution: "@e2e/top-level-await@workspace:e2e/top-level-await"
dependencies:
"@graphql-mesh/compose-cli": "workspace:*"
"@graphql-mesh/serve-cli": "workspace:*"
graphql: "npm:16.9.0"
languageName: unknown
linkType: soft

"@e2e/type-merging-batching@workspace:e2e/type-merging-batching":
version: 0.0.0-use.local
resolution: "@e2e/type-merging-batching@workspace:e2e/type-merging-batching"
Expand Down
Loading