Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
382fb8c
[http-specs] Add http-specs tests for CSV encoding on model propertie…
Copilot Dec 4, 2025
869089f
Add transformer library to compiler package
FionaBronwen Dec 4, 2025
3a1ac82
Bump jws from 3.2.2 to 3.2.3 in /packages/http-client-csharp (#9140)
dependabot[bot] Dec 4, 2025
3168ad7
Add better diagnostic handling and various cleanup
FionaBronwen Dec 4, 2025
9d36399
Cleanup some Java TypeSpec tests (#9132)
alzimmermsft Dec 5, 2025
f3d792b
Add security warning to tsp init documentation and CLI for external t…
Copilot Dec 5, 2025
e235fbd
Multi-level discriminator handling improvements (#9119)
radhgupta Dec 5, 2025
e50ab7c
Upgrade pools (#9009)
timotheeguerin Dec 5, 2025
7af8f6d
[Python] Fix serialization name for multipart (#9138)
msyyc Dec 8, 2025
b1176f5
http-client-java, fix FinalResult model incorrectly removed (#9145)
weidongxu-microsoft Dec 8, 2025
27036b8
[Python] Fix multipart when files part is optional (#9144)
msyyc Dec 8, 2025
adf2cd5
fix: import OpenAPI 3.1/3.2 contentEncoding base64 as bytes with @enc…
Copilot Dec 8, 2025
dd00983
Add External property to InputType to preserve original type informat…
Copilot Dec 8, 2025
679a025
Fix external metadata (#9161)
JoshLove-msft Dec 8, 2025
c3eb172
fix(openapi3): emit @encode decorator for anyOf/oneOf with unixtime f…
Copilot Dec 9, 2025
6439e09
Add release notes for 1.7.0 release (#9165)
timotheeguerin Dec 9, 2025
ec260f5
Use CSharpType rather than Type for serialization hooks (#9166)
JoshLove-msft Dec 9, 2025
12a7ed4
Remove test branch (#9157)
timotheeguerin Dec 9, 2025
1c3426f
fix: respect custom property & field nullability (#9167)
jorgerangel-msft Dec 9, 2025
fecf4f2
Bump version for typespec release 1.7.0 (#9168)
timotheeguerin Dec 9, 2025
7157290
[python] release new version (#9170)
msyyc Dec 10, 2025
a5efa3c
Make equality comparer for CSharpType public (#9169)
jorgerangel-msft Dec 10, 2025
cea1774
Don't call non-existent deserialize method for framework types (#9177)
JoshLove-msft Dec 10, 2025
4a728cb
hsjs: fix handling of optional query/header params (#9176)
witemple-msft Dec 10, 2025
3317b78
Allow customization of base model when spec defines a base model (#9181)
Copilot Dec 11, 2025
d1110c0
[Specs] add case for constant query (#8836)
msyyc Dec 11, 2025
9aea80e
[Python] fix bad indent in docstring (#8927)
msyyc Dec 11, 2025
2ab20b8
Fix for getting BinaryData from JsonElement (#9182)
JoshLove-msft Dec 11, 2025
d2bd68e
Always pass CSharpType to DeserializeJsonValue (#9191)
JoshLove-msft Dec 11, 2025
b3e4cdb
Upgrade @typespec/compiler and related libraries to latest versions i…
Copilot Dec 12, 2025
d762544
http-client-java, bump typespec and tcgc versions (#9172)
haolingdong-msft Dec 12, 2025
08179f6
[Python] Support encode for array of string in serialization and dese…
msyyc Dec 12, 2025
85a59e5
Fix problem pane (#9174)
timotheeguerin Dec 12, 2025
69e56d2
Refactor enum creation (#9197)
jorgerangel-msft Dec 12, 2025
4cf169f
Alloy 0.22 (#9202)
bterlson Dec 12, 2025
a2c1939
[python] release new version (#9204)
msyyc Dec 15, 2025
00c7c67
Backmerge/release/dec 2025 2025 12 15 (#9214)
timotheeguerin Dec 15, 2025
91cb685
Fix website accesibility issues (#9175)
timotheeguerin Dec 15, 2025
d7f969b
Mutator and HTTP Canonicalization updates (#9141)
bterlson Dec 15, 2025
93a9c0e
[Python] fix client default value for special headers (#9219)
msyyc Dec 16, 2025
1564e6c
Merge branch 'main' into fionabronwen/transformer-base
FionaBronwen Dec 18, 2025
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
25 changes: 25 additions & 0 deletions packages/compiler/src/config/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,30 @@ export const TypeSpecConfigJsonSchema: JSONSchemaType<TypeSpecRawConfig> = {
},
},
} as any, // ajv type system doesn't like the string templates
transformer: {
type: "object",
nullable: true,
required: [],
additionalProperties: false,
properties: {
extends: {
type: "array",
nullable: true,
items: { type: "string" },
},
enable: {
type: "object",
required: [],
nullable: true,
additionalProperties: { type: "boolean" },
},
disable: {
type: "object",
required: [],
nullable: true,
additionalProperties: { type: "string" },
},
},
} as any, // ajv type system doesn't like the string templates
},
};
11 changes: 10 additions & 1 deletion packages/compiler/src/config/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Diagnostic, RuleRef } from "../core/types.js";
import type { Diagnostic, RuleRef, TransformSetRef } from "../core/types.js";
import type { YamlScript } from "../yaml/types.js";

/**
Expand Down Expand Up @@ -69,6 +69,8 @@ export interface TypeSpecConfig {
options?: Record<string, EmitterOptions>;

linter?: LinterConfig;

transformer?: TransformerConfig;
}

/**
Expand All @@ -88,6 +90,7 @@ export interface TypeSpecRawConfig {
options?: Record<string, EmitterOptions>;

linter?: LinterConfig;
transformer?: TransformerConfig;
}

export interface ConfigEnvironmentVariable {
Expand All @@ -107,3 +110,9 @@ export interface LinterConfig {
enable?: Record<RuleRef, boolean>;
disable?: Record<RuleRef, string>;
}

export interface TransformerConfig {
extends?: TransformSetRef[];
enable?: Record<TransformSetRef, boolean>;
disable?: Record<TransformSetRef, string>;
}
12 changes: 12 additions & 0 deletions packages/compiler/src/core/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
LinterRuleDefinition,
PackageFlags,
StateDef,
TransformDefinition,
TransformerDefinition,
TypeSpecLibrary,
TypeSpecLibraryDef,
} from "./types.js";
Expand Down Expand Up @@ -116,6 +118,16 @@ export function createLinterRule<const N extends string, const T extends Diagnos
return definition;
}

export function defineTransformer(def: TransformerDefinition): TransformerDefinition {
return def;
}

/** Create a new transform. */
export function createTransform<const N extends string>(definition: TransformDefinition<N>) {
compilerAssert(!definition.name.includes("/"), "Transform name cannot contain a '/'.");
return definition;
}

/**
* Set the TypeSpec namespace for that function.
* @param namespace Namespace string (e.g. "Foo.Bar")
Expand Down
34 changes: 34 additions & 0 deletions packages/compiler/src/core/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,40 @@ const diagnostics = {
},
},

/**
* Transformer
*/
"invalid-transform-ref": {
severity: "error",
messages: {
default: paramMessage`Reference "${"ref"}" is not a valid reference to a transform or transform set. It must be in the following format: "<library-name>/<transform-name>"`,
},
},
"unknown-transform": {
severity: "warning",
messages: {
default: paramMessage`Transform "${"transformName"}" is not found in library "${"libraryName"}"`,
},
},
"unknown-transform-set": {
severity: "warning",
messages: {
default: paramMessage`Transform set "${"transformSetName"}" is not found in library "${"libraryName"}"`,
},
},
"transform-enabled-disabled": {
severity: "warning",
messages: {
default: paramMessage`Transform "${"transformName"}" has been enabled and disabled in the same transform set.`,
},
},
Comment on lines +773 to +790

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the linter, these are errors, not warning`s. Should we be doing the same here?

"transform-engine-error": {
severity: "error",
messages: {
default: paramMessage`Failed to create mutation engine for transform "${"transformId"}": ${"error"}`,
},
},

/**
* Formatter
*/
Expand Down
5 changes: 4 additions & 1 deletion packages/compiler/src/core/options.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EmitterOptions, TypeSpecConfig } from "../config/types.js";
import { LinterRuleSet, ParseOptions } from "./types.js";
import { LinterRuleSet, ParseOptions, TransformSet } from "./types.js";

export interface CompilerOptions {
miscOptions?: Record<string, unknown>;
Expand Down Expand Up @@ -68,6 +68,9 @@ export interface CompilerOptions {
/** Ruleset to enable for linting. */
linterRuleSet?: LinterRuleSet;

/** Transform set to enable for transformation. */
transformSet?: TransformSet;

/** @internal */
readonly configFile?: TypeSpecConfig;
}
47 changes: 42 additions & 5 deletions packages/compiler/src/core/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ import {
} from "./source-loader.js";
import { createStateAccessors } from "./state-accessors.js";
import { ComplexityStats, RuntimeStats, Stats, startTimer, time, timeAsync } from "./stats.js";
import {
builtInTransformerLibraryName,
createBuiltInTransformerLibrary,
createTransformer,
resolveTransformerDefinition,
} from "./transformer.js";
import {
CompilerHost,
Diagnostic,
Expand All @@ -52,8 +58,8 @@ import {
EmitterFunc,
Entity,
JsSourceFileNode,
LibraryInstance,
LibraryMetadata,
LinterLibraryInstance,
LiteralType,
LocationContext,
LogSink,
Expand All @@ -68,6 +74,7 @@ import {
SyntaxKind,
TemplateInstanceTarget,
Tracer,
TransformerLibraryInstance,
Type,
TypeSpecLibrary,
TypeSpecScriptNode,
Expand Down Expand Up @@ -136,13 +143,20 @@ export interface Program {
readonly projectRoot: string;
}

export interface TransformedProgram extends Program {
/**
* Result from running transformers, including mutation engines for accessing transformed types.
*/
readonly transformerResult?: import("./transformer.js").TransformerResult;
}

interface EmitterRef {
emitFunction: EmitterFunc;
main: string;
metadata: LibraryMetadata;
emitterOutputDir: string;
options: Record<string, unknown>;
readonly library: LibraryInstance;
readonly library: LinterLibraryInstance & TransformerLibraryInstance;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm do we need to make this change? This implies that every library has a linter and a transformer, which I don't think is true?

I'd expect this to either just be LibraryInstance (with linter removed), or LibraryInstance | LinterLibraryInstance | TransformerLibraryInstance.

}

interface Validator {
Expand Down Expand Up @@ -207,7 +221,7 @@ async function createProgram(
mainFile: string,
options: CompilerOptions = {},
oldProgram?: Program,
): Promise<{ program: Program; shouldAbort: boolean }> {
): Promise<{ program: TransformedProgram; shouldAbort: boolean }> {
const runtimeStats: Partial<RuntimeStats> = {};
const validateCbs: Validator[] = [];
const stateMaps = new Map<symbol, Map<Type, unknown>>();
Expand Down Expand Up @@ -303,6 +317,16 @@ async function createProgram(
program.reportDiagnostics(await linter.extendRuleSet(options.linterRuleSet));
}

const transformer = createTransformer(program, (name) => loadLibrary(basedir, name));
// Register built-in transformer library (currently empty placeholder)
transformer.registerTransformLibrary(
builtInTransformerLibraryName,
createBuiltInTransformerLibrary(),
);
Comment on lines +322 to +325

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would the builtin transformer library be? The builtin linter library enables a couple of standard rules.

Are you thinking of there being "standard" transforms, e.g. @visibility?

if (options.transformSet) {
program.reportDiagnostics(await transformer.extendTransformSet(options.transformSet));
}

program.checker = createChecker(program, resolver);
runtimeStats.checker = time(() => program.checker.checkProgram());

Expand All @@ -329,7 +353,17 @@ async function createProgram(
runtimeStats.linter = lintResult.stats.runtime;
program.reportDiagnostics(lintResult.diagnostics);

return { program, shouldAbort: false };
// Transform stage
const transformResult = transformer.transform();
runtimeStats.transformer = transformResult.stats.runtime;
program.reportDiagnostics(transformResult.diagnostics);

// Attach transformer result to the program so consumers can access mutation engines
const transformedProgram: TransformedProgram = Object.assign(transformResult.program, {
transformerResult: transformResult,
});

return { program: transformedProgram, shouldAbort: false };

/**
* Validate the libraries loaded during the compilation process are compatible.
Expand Down Expand Up @@ -507,7 +541,7 @@ async function createProgram(
async function loadLibrary(
basedir: string,
libraryNameOrPath: string,
): Promise<LibraryInstance | undefined> {
): Promise<(LinterLibraryInstance & TransformerLibraryInstance) | undefined> {
const [resolution, diagnostics] = await resolveEmitterModuleAndEntrypoint(
basedir,
libraryNameOrPath,
Expand All @@ -522,11 +556,14 @@ async function createProgram(
const libDefinition: TypeSpecLibrary<any> | undefined = entrypoint?.esmExports.$lib;
const metadata = computeLibraryMetadata(module, libDefinition);
const linterDef = entrypoint?.esmExports.$linter;
const transformerDef = entrypoint?.esmExports.$transformer;
return {
...resolution,
metadata,
definition: libDefinition,
linter: linterDef && resolveLinterDefinition(libraryNameOrPath, linterDef),
transformer:
transformerDef && resolveTransformerDefinition(libraryNameOrPath, transformerDef),
};
}

Expand Down
7 changes: 7 additions & 0 deletions packages/compiler/src/core/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ export interface RuntimeStats {
[rule: string]: number;
};
};
transformer: {
enabledTransforms: readonly string[];
total: number;
engineCreation: {
[transformId: string]: number;
};
};
emit: {
total: number;
emitters: {
Expand Down
Loading