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

Added emitter support to init template #5415

Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: internal
packages:
- typespec-vscode
---

Updated the init project template to include the new emitter definition
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@typespec/compiler"
---

Added support for emitter selections for init template.
37 changes: 37 additions & 0 deletions packages/compiler/src/init/init-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export interface InitTemplate {
*/
libraries?: InitTemplateLibrary[];

/**
* List of emitters to include
*/
emitters?: Record<string, EmitterTemplate>;

/**
* Config
*/
Expand All @@ -55,6 +60,22 @@ export interface InitTemplate {
files?: InitTemplateFile[];
}

/**
* Describes emitter dependencies that will be added to the generated project.
*/
export interface EmitterTemplate {
/** Emitter Selection Description */
description?: string;
/** Whether emitter is selected by default in the list */
selected?: boolean;
/** Optional emitter Options to populate the tspconfig.yaml */
options?: any;
/** Optional message to display to the user post creation */
message?: string;
/** Optional specific emitter version. `latest` if not specified */
version?: string;
}

/**
* Describes a library dependency that will be added to the generated project.
*/
Expand Down Expand Up @@ -99,6 +120,22 @@ export const InitTemplateSchema: JSONSchemaType<InitTemplate> = {
},
nullable: true,
},
emitters: {
type: "object",
nullable: true,
additionalProperties: {
type: "object",
properties: {
description: { type: "string", nullable: true },
selected: { type: "boolean", nullable: true },
options: {} as any,
message: { type: "string", nullable: true },
version: { type: "string", nullable: true },
},
required: [],
},
required: [],
},
skipCompilerPackage: { type: "boolean", nullable: true },
config: { nullable: true, ...TypeSpecConfigJsonSchema },
inputs: {
Expand Down
43 changes: 42 additions & 1 deletion packages/compiler/src/init/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { MANIFEST } from "../manifest.js";
import { readUrlOrPath } from "../utils/misc.js";
import { getTypeSpecCoreTemplates } from "./core-templates.js";
import { validateTemplateDefinitions, ValidationResult } from "./init-template-validate.js";
import { InitTemplate, InitTemplateLibrarySpec } from "./init-template.js";
import { EmitterTemplate, InitTemplate, InitTemplateLibrarySpec } from "./init-template.js";
import { makeScaffoldingConfig, normalizeLibrary, scaffoldNewProject } from "./scaffold.js";

export interface InitTypeSpecProjectOptions {
Expand Down Expand Up @@ -66,6 +66,7 @@ export async function initTypeSpecProject(
]);

const libraries = await selectLibraries(template);
const emitters = await selectEmitters(template);
const parameters = await promptCustomParameters(template);
const scaffoldingConfig = makeScaffoldingConfig(template, {
baseUri: result.baseUri,
Expand All @@ -75,6 +76,7 @@ export async function initTypeSpecProject(
folderName,
parameters,
includeGitignore,
emitters,
});

await scaffoldNewProject(host, scaffoldingConfig);
Expand All @@ -86,6 +88,18 @@ export async function initTypeSpecProject(

// eslint-disable-next-line no-console
console.log(pc.green("Project created successfully."));

if (Object.values(emitters).some((emitter) => emitter.message !== undefined)) {
// eslint-disable-next-line no-console
console.log(pc.yellow("\nPlease review the following messages from emitters:"));

for (const key of Object.keys(emitters)) {
if (emitters[key].message) {
// eslint-disable-next-line no-console
console.log(` ${key}: \n\t${emitters[key].message}`);
}
}
}
}

async function promptCustomParameters(template: InitTemplate): Promise<Record<string, any>> {
Expand Down Expand Up @@ -235,6 +249,33 @@ async function validateTemplate(template: any, loaded: LoadedTemplate): Promise<
return true;
}

async function selectEmitters(template: InitTemplate): Promise<Record<string, EmitterTemplate>> {
if (!template.emitters) {
return {};
}

const promptList = [...Object.entries(template.emitters)].map(([name, emitter]) => {
return {
title: name,
description: emitter.description,
selected: emitter.selected ?? false,
};
});

const { emitters } = await prompts({
type: "multiselect",
name: "emitters",
message: "Select emitters?",
choices: promptList,
});

const selectedEmitters = [...Object.entries(template.emitters)].filter((_, index) =>
emitters.includes(index),
);

return Object.fromEntries(selectedEmitters);
}

async function selectLibraries(template: InitTemplate): Promise<InitTemplateLibrarySpec[]> {
if (template.libraries === undefined || template.libraries.length === 0) {
return [];
Expand Down
36 changes: 30 additions & 6 deletions packages/compiler/src/init/scaffold.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ export interface ScaffoldingConfig {
* Custom parameters provided in the tempalates.
*/
parameters: Record<string, any>;

/**
* Selected emitters the tempalates.
*/
emitters: Record<string, any>;
}

export function normalizeLibrary(library: InitTemplateLibrary): InitTemplateLibrarySpec {
Expand All @@ -73,6 +78,7 @@ export function makeScaffoldingConfig(
folderName: config.folderName ?? "",
parameters: config.parameters ?? {},
includeGitignore: config.includeGitignore ?? true,
emitters: config.emitters ?? {},
...config,
};
}
Expand Down Expand Up @@ -113,8 +119,13 @@ async function writePackageJson(host: CompilerHost, config: ScaffoldingConfig) {
}

for (const library of config.libraries) {
peerDependencies[library.name] = await getLibraryVersion(library);
devDependencies[library.name] = await getLibraryVersion(library);
peerDependencies[library.name] = await getPackageVersion(library);
devDependencies[library.name] = await getPackageVersion(library);
}

for (const key of Object.keys(config.emitters)) {
peerDependencies[key] = await getPackageVersion(config.emitters[key]);
devDependencies[key] = await getPackageVersion(config.emitters[key]);
}

const packageJson: PackageJson = {
Expand Down Expand Up @@ -154,7 +165,20 @@ async function writeConfig(host: CompilerHost, config: ScaffoldingConfig) {
if (isFileSkipGeneration(TypeSpecConfigFilename, config.template.files ?? [])) {
return;
}
const content = config.template.config ? stringify(config.template.config) : placeholderConfig;

let content: string = placeholderConfig;
if (config.template.config !== undefined && Object.keys(config.template.config).length > 0) {
content = stringify(config.template.config);
} else if (Object.keys(config.emitters).length > 0) {
const emitters = Object.keys(config.emitters);
const options = Object.fromEntries(
Object.entries(config.emitters).map(([key, emitter]) => [key, emitter.options]),
);
content = stringify({
emit: emitters,
options: Object.keys(options).length > 0 ? options : undefined,
});
}
return host.writeFile(joinPaths(config.directory, TypeSpecConfigFilename), content);
}

Expand All @@ -165,7 +189,7 @@ async function writeMain(host: CompilerHost, config: ScaffoldingConfig) {
const dependencies: Record<string, string> = {};

for (const library of config.libraries) {
dependencies[library.name] = await getLibraryVersion(library);
dependencies[library.name] = await getPackageVersion(library);
}

const lines = [...config.libraries.map((x) => `import "${x.name}";`), ""];
Expand Down Expand Up @@ -220,7 +244,7 @@ async function writeFile(
return host.writeFile(joinPaths(config.directory, file.destination), content);
}

async function getLibraryVersion(library: InitTemplateLibrarySpec): Promise<string> {
async function getPackageVersion(packageInfo: { version?: string }): Promise<string> {
// TODO: Resolve 'latest' version from npm, issue #1919
return library.version ?? "latest";
return packageInfo.version ?? "latest";
}
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ export async function createTypeSpecProject(client: TspLanguageClient | undefine
return;
}

// TODO: add support for emitters picking
const initTemplateConfig: InitProjectConfig = {
template: info.template!,
directory: selectedRootFolder,
Expand All @@ -171,6 +172,7 @@ export async function createTypeSpecProject(client: TspLanguageClient | undefine
parameters: inputs ?? {},
includeGitignore: includeGitignore,
libraries: librariesToInclude,
emitters: {},
};
const initResult = await initProject(client, initTemplateConfig);
if (!initResult) {
Expand Down
Loading