Skip to content
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
53 changes: 53 additions & 0 deletions graphql/codegen/src/core/codegen/barrel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,59 @@ export function generateRootBarrel(options: RootBarrelOptions = {}): string {
return generateCode(statements);
}

// ============================================================================
// Multi-target root barrel (re-exports each target as a namespace)
// ============================================================================

/** JS reserved words that need an alias when used as export names */
const JS_RESERVED = new Set([
'abstract', 'arguments', 'await', 'boolean', 'break', 'byte', 'case', 'catch',
'char', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do',
'double', 'else', 'enum', 'eval', 'export', 'extends', 'false', 'final',
'finally', 'float', 'for', 'function', 'goto', 'if', 'implements', 'import',
'in', 'instanceof', 'int', 'interface', 'let', 'long', 'native', 'new',
'null', 'package', 'private', 'protected', 'public', 'return', 'short',
'static', 'super', 'switch', 'synchronized', 'this', 'throw', 'throws',
'transient', 'true', 'try', 'typeof', 'var', 'void', 'volatile', 'while',
'with', 'yield',
]);

/**
* Generate a root index.ts for multi-target output that re-exports each
* target as a namespace.
*
* Example output:
* export * as admin from './admin';
* export * as auth from './auth';
* export * as public_ from './public';
*/
export function generateMultiTargetBarrel(targetNames: string[]): string {
const statements: t.Statement[] = [];

for (const name of targetNames) {
const alias = JS_RESERVED.has(name) ? `${name}_` : name;
const exportDecl = t.exportNamedDeclaration(
null,
[t.exportNamespaceSpecifier(t.identifier(alias))],
t.stringLiteral(`./${name}`),
);
statements.push(exportDecl);
}

if (statements.length > 0) {
addJSDocComment(statements[0], [
'@constructive-io/sdk',
'',
'Auto-generated GraphQL types and ORM client.',
'Run `pnpm run generate` to populate this package from the schema files.',
'',
'@generated by @constructive-io/graphql-codegen',
]);
}

return generateCode(statements);
}

// ============================================================================
// Custom operation barrels (includes both table and custom hooks)
// ============================================================================
Expand Down
25 changes: 22 additions & 3 deletions graphql/codegen/src/core/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type { CliConfig, DbConfig, GraphQLSDKConfigTarget, PgpmConfig, SchemaCon
import { getConfigOptions } from '../types/config';
import type { Operation, Table, TypeRegistry } from '../types/schema';
import { generate as generateReactQueryFiles } from './codegen';
import { generateRootBarrel } from './codegen/barrel';
import { generateRootBarrel, generateMultiTargetBarrel } from './codegen/barrel';
import { generateCli as generateCliFiles, generateMultiTargetCli } from './codegen/cli';
import type { MultiTargetCliTarget } from './codegen/cli';
import {
Expand Down Expand Up @@ -827,16 +827,35 @@ export async function generateMulti(
}
}

// Generate root-root README if multi-target
// Generate root-root README and barrel if multi-target
if (names.length > 1 && targetInfos.length > 0 && !dryRun) {
const rootReadme = generateRootRootReadme(targetInfos);
const { writeGeneratedFiles: writeFiles } = await import('./output');

const rootReadme = generateRootRootReadme(targetInfos);
await writeFiles(
[{ path: rootReadme.fileName, content: rootReadme.content }],
'.',
[],
{ pruneStaleFiles: false },
);

// Write a root barrel (index.ts) that re-exports each target as a
// namespace so the package has a single entry-point. Derive the
// common output root from the first target's output path.
const successfulNames = results
.filter((r) => r.result.success)
.map((r) => r.name);
if (successfulNames.length > 0) {
const firstOutput = getConfigOptions(configs[successfulNames[0]]).output;
const outputRoot = path.dirname(firstOutput);
const barrelContent = generateMultiTargetBarrel(successfulNames);
await writeFiles(
[{ path: 'index.ts', content: barrelContent }],
outputRoot,
[],
{ pruneStaleFiles: false },
);
}
}

} finally {
Expand Down
Loading