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
33 changes: 2 additions & 31 deletions packages/typespec-powershell/src/emitter.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,14 @@
import { Program, EmitContext, listServices, emitFile, resolvePath } from "@typespec/compiler";
import { serialize, deserialize } from '@azure-tools/codegen';
import { serialize } from '@azure-tools/codegen';
import { createSdkContext } from "@azure-tools/typespec-client-generator-core";
// Following files finally should be imported from @autorest/powershell
// import { ModelState } from "./powershell/utils/model-state.js";
// import { PwshModel } from "./powershell/utils/PwshModel.js";
import { getClients } from "./utils/clientUtils.js";
import { transformPwshModel } from "./convertor/convertor.js";
import { PSOptions } from "./types/interfaces.js";
import { loadConfiguration } from "./utils/configuration.js";
import { generatePwshModule } from "@autorest/powershell";
import { readFileSync } from "fs";
import path from "path";
import { fileURLToPath } from "url";

// Function to recursively interpolate placeholders in an object
function interpolatePlaceholders(obj: any, values: any) {
for (const key in obj) {
if (typeof obj[key] === 'string') {
obj[key] = obj[key].replace(/{([^}]+)}/g, (match: string, placeholder: string) => values[placeholder]);
} else if (typeof obj[key] === 'object') {
interpolatePlaceholders(obj[key], values);
}
}
}

//load configuration from configuration.md
function loadConfiguration(emitterOptions: Record<string, any>): Record<string, any> {
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const configPath = path.join(__dirname, '../../configuration.yaml');
const configuration = deserialize<Record<string, any>>(readFileSync(configPath, 'utf8'), configPath);
// Define the values for interpolation
const interpolationValues = {
'module-name': emitterOptions['module-name'] ?? configuration['module-name'],
'service-name': emitterOptions['service-name'] ?? configuration['service-name']
};
interpolatePlaceholders(configuration, interpolationValues);
// If there is overlap between the configuration and the emitter options, the emitter options will take precedence
return { ...configuration, ...emitterOptions };
}

export async function $onEmit(context: EmitContext) {
const program: Program = context.program;
Expand Down
60 changes: 60 additions & 0 deletions packages/typespec-powershell/src/utils/configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { deserialize } from '@azure-tools/codegen';
import { readFileSync } from "fs";
import path from "path";
import { fileURLToPath } from "url";

function isPlainObject(value: any): value is Record<string, any> {
return value !== null && typeof value === 'object' && !Array.isArray(value);
}

// Recursively merges source into target. Plain objects merge by key; arrays,
// primitives and null in source replace the target value. Inputs are not mutated.
export function deepMerge(target: any, source: any): any {
if (!isPlainObject(target) || !isPlainObject(source)) {
return source === undefined ? target : source;
}

const result: Record<string, any> = { ...target };
for (const key of Object.keys(source)) {
const sourceValue = source[key];
if (sourceValue === undefined) {
continue;
}
result[key] = isPlainObject(result[key]) && isPlainObject(sourceValue)
? deepMerge(result[key], sourceValue)
: sourceValue;
}
return result;
}

// Recursively interpolate {placeholder} tokens in string values.
export function interpolatePlaceholders(obj: any, values: Record<string, any>): void {
for (const key in obj) {
if (typeof obj[key] === 'string') {
obj[key] = obj[key].replace(/{([^}]+)}/g, (match: string, placeholder: string) => values[placeholder]);
} else if (obj[key] !== null && typeof obj[key] === 'object') {
interpolatePlaceholders(obj[key], values);
}
}
}

// Load configuration.yaml and merge emitter options over it, with emitter
// options taking precedence at every level of nested mappings.
export function loadConfiguration(emitterOptions: Record<string, any>): Record<string, any> {
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const configPath = path.join(__dirname, '../../../configuration.yaml');
const configuration = deserialize<Record<string, any>>(readFileSync(configPath, 'utf8'), configPath);

// Emitter options take precedence over the defaults, merged recursively so
// partial nested mappings (e.g. metadata) keep unspecified defaults.
const merged = deepMerge(configuration, emitterOptions);

const interpolationValues = {
'module-name': merged['module-name'],
'service-name': merged['service-name']
};
interpolatePlaceholders(merged, interpolationValues);

return merged;
}
Loading