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
5 changes: 5 additions & 0 deletions .changeset/deep-houses-exist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hey-api/shared": patch
---

**types**: update project meta types
5 changes: 5 additions & 0 deletions .changeset/empty-stars-crash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hey-api/codegen-core": minor
---

**types**: rename `ProjectRenderMeta` to `ProjectMeta` and key it by language
2 changes: 1 addition & 1 deletion packages/codegen-core/src/__tests__/exports.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export type _TypeExports = [
index.NodeScope,
index.Output,
index.Project,
index.ProjectRenderMeta,
index.ProjectMeta,
index.Ref<any>,
index.Refs<any>,
index.RenderContext,
Expand Down
8 changes: 2 additions & 6 deletions packages/codegen-core/src/__tests__/project.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,12 @@ describe('Project', () => {

it('passes correct ctx to renderer.render()', () => {
const p = makeProject();
p.render({ hello: true });
p.render();

const file = [...p.files.registered()][0]!;
const renderer = file.renderer!;

expect(renderer.render).toHaveBeenCalledWith({
file,
meta: { hello: true },
project: p,
});
expect(renderer.render).toHaveBeenCalledWith({ file, project: p });
});
});

Expand Down
11 changes: 6 additions & 5 deletions packages/codegen-core/src/extensions.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import type { Language } from './languages/types';

/**
* Arbitrary metadata passed to the project's render function.
* Arbitrary project metadata.
*
* Implementers should extend this interface for their own needs.
*/
export interface IProjectRenderMeta {
[key: string]: unknown;
}
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface IProjectMeta extends Partial<Record<Language, unknown>> {}

/**
* Additional metadata about the symbol.
* Arbitrary symbol metadata.
*
* Implementers should extend this interface for their own needs.
*/
Expand Down
5 changes: 1 addition & 4 deletions packages/codegen-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ export { nodeBrand, symbolBrand } from './brands';
export { detectInteractiveSession } from './config/interactive';
export { loadConfigFile } from './config/load';
export { mergeConfigs } from './config/merge';
export type {
IProjectRenderMeta as ProjectRenderMeta,
ISymbolMeta as SymbolMeta,
} from './extensions';
export type { IProjectMeta as ProjectMeta, ISymbolMeta as SymbolMeta } from './extensions';
export { File } from './files/file';
export type { IFileIn as FileIn } from './files/types';
export { isNode, isNodeRef, isSymbol, isSymbolRef } from './guards';
Expand Down
13 changes: 11 additions & 2 deletions packages/codegen-core/src/planner/analyzer.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { IProjectMeta } from '../extensions';
import { isNodeRef, isSymbolRef } from '../guards';
import type { INode, NodeRelationship } from '../nodes/node';
import { fromRef, isRef, ref } from '../refs/refs';
Expand All @@ -15,12 +16,15 @@ export class AnalysisContext implements IAnalysisContext {
*/
private _parentStack: Array<INode> = [];

/** Arbitrary project metadata. */
meta: IProjectMeta;
scope: Scope;
scopes: Scope = createScope();
symbol?: Symbol;

constructor(node: INode) {
constructor(node: INode, meta: IProjectMeta) {
this._parentStack.push(node);
this.meta = meta;
this.scope = this.scopes;
this.symbol = node.symbol;
}
Expand Down Expand Up @@ -149,14 +153,19 @@ export class AnalysisContext implements IAnalysisContext {
}

export class Analyzer {
private readonly meta: IProjectMeta;
private nodeCache = new WeakMap<INode, AnalysisContext>();

constructor(meta: IProjectMeta) {
this.meta = meta;
}

analyzeNode(node: INode): AnalysisContext {
const cached = this.nodeCache.get(node);
if (cached) return cached;

node.root = true;
const ctx = new AnalysisContext(node);
const ctx = new AnalysisContext(node, this.meta);
node.analyze(ctx);

this.nodeCache.set(node, ctx);
Expand Down
12 changes: 6 additions & 6 deletions packages/codegen-core/src/planner/planner.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import path from 'node:path';

import type { ExportModule, ImportModule } from '../bindings';
import type { IProjectRenderMeta } from '../extensions';
import type { File } from '../files/file';
import type { INode } from '../nodes/node';
import { canDeclarationsShareIdentifier } from '../project/namespace';
Expand All @@ -18,22 +17,23 @@ import { createScope, registerName } from './scope';
const isTypeOnlyKind = (kind: SymbolKind) => kind === 'type' || kind === 'interface';

export class Planner {
private readonly analyzer = new Analyzer();
private readonly analyzer: Analyzer;
private readonly cacheResolvedNames = new Set<number>();
private readonly project: IProject;

constructor(project: IProject) {
this.analyzer = new Analyzer(project.meta);
this.project = project;
}

/**
* Executes the planning phase for the project.
*/
plan(meta?: IProjectRenderMeta) {
plan() {
this.cacheResolvedNames.clear();
this.allocateFiles();
this.assignLocalNames();
this.resolveFilePaths(meta);
this.resolveFilePaths();
this.planExports();
this.planImports();
}
Expand Down Expand Up @@ -112,7 +112,7 @@ export class Planner {
*
* Resolves final paths relative to the project's root directory.
*/
private resolveFilePaths(meta?: IProjectRenderMeta): void {
private resolveFilePaths(): void {
for (const file of this.project.files.registered()) {
if (file.external) {
file.setFinalPath(file.logicalFilePath);
Expand All @@ -124,7 +124,7 @@ export class Planner {
if (finalPath) {
file.setFinalPath(path.resolve(this.project.root, finalPath));
}
const ctx: RenderContext = { file, meta, project: this.project };
const ctx: RenderContext = { file, project: this.project };
const renderer = this.project.renderers.find((r) => r.supports(ctx));
if (renderer) file.setRenderer(renderer);
}
Expand Down
3 changes: 3 additions & 0 deletions packages/codegen-core/src/planner/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { IProjectMeta } from '../extensions';
import type { Ref } from '../refs/types';
import type { Symbol } from '../symbols/symbol';
import type { NameScopes, Scope } from './scope';
Expand All @@ -20,6 +21,8 @@ export interface IAnalysisContext {
injectChildren(input: Input): void;
/** Get local names in the current scope. */
localNames(scope: Scope): NameScopes;
/** Arbitrary project metadata. */
meta: IProjectMeta;
/** Pop the current local scope. */
popScope(): void;
/** Push a new local scope. */
Expand Down
16 changes: 9 additions & 7 deletions packages/codegen-core/src/project/project.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'node:path';

import type { IProjectRenderMeta } from '../extensions';
import type { IProjectMeta } from '../extensions';
import { FileRegistry } from '../files/registry';
import { defaultExtensions } from '../languages/extensions';
import { defaultModuleEntryNames } from '../languages/modules';
Expand All @@ -19,6 +19,7 @@ export class Project implements IProject {
private _isPlanned = false;

readonly files: FileRegistry;
readonly meta: IProjectMeta;
readonly nodes = new NodeRegistry();
readonly symbols = new SymbolRegistry();

Expand All @@ -42,8 +43,9 @@ export class Project implements IProject {
| 'nameConflictResolvers'
| 'renderers'
> &
Pick<IProject, 'root'>,
Pick<IProject, 'root'> & { meta?: IProjectMeta },
) {
this.meta = args.meta ?? {};
const fileName = args.fileName;
this.defaultFileName = args.defaultFileName ?? 'main';
this.defaultNameConflictResolver =
Expand All @@ -66,18 +68,18 @@ export class Project implements IProject {
this.root = path.resolve(args.root).replace(/[/\\]+$/, '');
}

plan(meta?: IProjectRenderMeta): void {
plan(): void {
if (this._isPlanned) return;
new Planner(this).plan(meta);
new Planner(this).plan();
this._isPlanned = true;
}

render(meta?: IProjectRenderMeta): ReadonlyArray<IOutput> {
if (!this._isPlanned) this.plan(meta);
render(): ReadonlyArray<IOutput> {
if (!this._isPlanned) this.plan();
const files: Array<IOutput> = [];
for (const file of this.files.registered()) {
if (!file.external && file.finalPath && file.renderer) {
const content = file.renderer.render({ file, meta, project: this });
const content = file.renderer.render({ file, project: this });
files.push({ content, path: file.finalPath });
}
}
Expand Down
8 changes: 5 additions & 3 deletions packages/codegen-core/src/project/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IProjectRenderMeta } from '../extensions';
import type { IProjectMeta } from '../extensions';
import type { IFileRegistry } from '../files/types';
import type { Extensions, ModuleEntryNames, NameConflictResolvers } from '../languages/types';
import type { INodeRegistry } from '../nodes/types';
Expand Down Expand Up @@ -42,6 +42,8 @@ export interface IProject {
readonly fileName?: (name: string) => string;
/** Centralized file registry for the project. */
readonly files: IFileRegistry;
/** Arbitrary project metadata. */
readonly meta: IProjectMeta;
/**
* Map of module entry names for each language.
*
Expand Down Expand Up @@ -74,7 +76,7 @@ export interface IProject {
* @param meta Arbitrary metadata.
* @returns void
*/
plan(meta?: IProjectRenderMeta): void;
plan(meta?: IProjectMeta): void;
/**
* Produces output representations for all files in the project.
*
Expand All @@ -83,7 +85,7 @@ export interface IProject {
* @example
* project.render().forEach(output => writeFile(output));
*/
render(meta?: IProjectRenderMeta): ReadonlyArray<IOutput>;
render(meta?: IProjectMeta): ReadonlyArray<IOutput>;
/**
* List of available renderers.
*
Expand Down
5 changes: 0 additions & 5 deletions packages/codegen-core/src/renderer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { IProjectRenderMeta } from './extensions';
import type { File } from './files/file';
import type { INode } from './nodes/node';
import type { IProject } from './project/types';
Expand All @@ -8,10 +7,6 @@ export interface RenderContext<Node extends INode = INode> {
* The current file.
*/
file: File<Node>;
/**
* Arbitrary metadata.
*/
meta?: IProjectRenderMeta;
/**
* The project the file belongs to.
*/
Expand Down
5 changes: 5 additions & 0 deletions packages/openapi-python/src/createClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ export async function createClient({
}
return name === '__init__' || name.endsWith(suffix) ? name : `${name}${suffix}`;
},
meta: {
python: {
version: config.output.pythonVersion,
},
},
nameConflictResolvers: config.output.nameConflictResolver
? {
python: config.output.nameConflictResolver,
Expand Down
14 changes: 5 additions & 9 deletions packages/openapi-python/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,10 @@ import '@hey-api/codegen-core';
import '@hey-api/shared';

declare module '@hey-api/codegen-core' {
interface ProjectRenderMeta {
/**
* If specified, this will be the file extension used when importing
* other modules. By default, we don't add a file extension and let the
* runtime resolve it.
*
* @default null
*/
importFileExtension?: AnyString | null;
interface ProjectMeta {
python?: {
version: PythonVersion;
};
}

interface SymbolMeta {
Expand Down Expand Up @@ -61,6 +56,7 @@ import colors from 'ansi-colors';
// @ts-expect-error
import colorSupport from 'color-support';

import type { PythonVersion } from './config/output/types';
import type { UserConfig } from './config/types';
import type { HeyApiClientHttpxPlugin } from './plugins/@hey-api/client-httpx';
import type { HeyApiSdkPlugin } from './plugins/@hey-api/sdk';
Expand Down
2 changes: 1 addition & 1 deletion packages/openapi-ts/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import '@hey-api/codegen-core';
import '@hey-api/shared';

declare module '@hey-api/codegen-core' {
interface ProjectRenderMeta {
interface ProjectMeta {
/**
* If specified, this will be the file extension used when importing
* other modules. By default, we don't add a file extension and let the
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/src/config/output/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { MaybeArray, MaybeFunc } from '@hey-api/types';

export type OutputHeader = MaybeFunc<
(
ctx: Pick<RenderContext, 'meta' | 'project'> &
ctx: Pick<RenderContext, 'project'> &
Pick<Partial<RenderContext>, 'file'> & {
/** The default header value. */
defaultValue: ReadonlyArray<string>;
Expand Down
Loading