Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
bc5eca1
feat(cli): auto-detect language for single-language custom templates
rohang9000 Aug 28, 2025
21c505f
Merge branch 'main' into fix/auto-detect-single-language-template
iankhou Sep 9, 2025
0bf2c03
chore(deps): upgrade dependencies (#839)
aws-cdk-automation Sep 10, 2025
0d194f9
chore(deps): upgrade dependencies (#841)
aws-cdk-automation Sep 11, 2025
6975eae
chore: enable trusted publishing for releases to npm and pypi (#842)
iliapolo Sep 11, 2025
9766049
chore: npm trusted publishing for jsii packages is ignored (#843)
iliapolo Sep 11, 2025
9def72c
chore(deps): upgrade dependencies (#844)
aws-cdk-automation Sep 11, 2025
2be5f7a
chore: bump node (and npm) version in publish jobs to allow for trust…
iliapolo Sep 11, 2025
14c188a
fix(cli): stack filter positional arg is not being respected (#846)
otaviomacedo Sep 12, 2025
1c4d691
chore(deps): upgrade dependencies (#847)
aws-cdk-automation Sep 15, 2025
30be191
chore: add close-stale-issues workflow (#849)
pahud Sep 16, 2025
2a428d7
chore: fix stale-issue-cleanup version (#851)
mrgrain Sep 18, 2025
0eaefff
fix(cloud-assembly-schema): `unconfiguredBehavesLike` contains info f…
rix0rrr Sep 18, 2025
17bd208
chore(cli): remove deprecated AWS SDK v3 utilities in favor of Smithy…
mrgrain Sep 18, 2025
30b99b4
chore: revert aws-actions/stale-issue-cleanup back to v6 (#853)
mrgrain Sep 18, 2025
421cff6
chore: fix codeowner team name (#856)
mrgrain Sep 19, 2025
f5c0f15
refactor(cli): property bag for refreshStacks function (#855)
dgandhi62 Sep 19, 2025
e6e8dbb
chore(deps): upgrade dependencies (#857)
aws-cdk-automation Sep 22, 2025
1808f42
fix(cli-integ): sporadic failures due to external causes (#897)
iliapolo Sep 29, 2025
96f6f0b
feat(toolkit-lib): add forceDeployment option to BootstrapOptions (#898)
mrgrain Sep 30, 2025
b5cb197
chore(cli): add cdk flags to readme command list (#899)
kaizencc Oct 6, 2025
c091f72
feat(cli): allow users to enable all feature flags that do not impact…
vivian12300 Oct 6, 2025
70b3dea
refactor(toolkit-lib): standardize confirmation requests to use Confi…
mrgrain Oct 6, 2025
325a749
chore(cli-lib-alpha): stop releasing deprecated package (#905)
mrgrain Oct 6, 2025
3406ff7
feat(integ-runner): use toolkit-lib as default engine (#906)
mrgrain Oct 6, 2025
07895ef
address my comments and increase test coverage for init.ts (97.81%)
iankhou Oct 7, 2025
7801fb2
Merge branch 'main' into fix/auto-detect-single-language-template
iankhou Oct 7, 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
55 changes: 51 additions & 4 deletions packages/aws-cdk/lib/commands/init/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export async function cliInit(options: CliInitOptions) {
const generateOnly = options.generateOnly ?? false;
const workDir = options.workDir ?? process.cwd();

// Show available templates if no type and no language provided (main branch logic)
// Show available templates only if no fromPath, type, or language provided
if (!options.fromPath && !options.type && !options.language) {
await printAvailableTemplates(ioHelper);
return;
Expand Down Expand Up @@ -298,6 +298,24 @@ async function hasLanguageFiles(directoryPath: string, extensions: string[]): Pr
return false;
}

/**
* Get file extensions for a specific language
* @param language - The programming language
* @returns Array of file extensions for the language
*/
function getLanguageExtensions(language: string): string[] {
const languageExtensions: Record<string, string[]> = {
typescript: ['.ts', '.js'],
javascript: ['.js'],
python: ['.py'],
java: ['.java'],
csharp: ['.cs'],
fsharp: ['.fs'],
go: ['.go'],
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Moved language extensions to new interface LanguageInfo, which is the new, consolidated source of truth.

};
return languageExtensions[language] || [];
}

/**
* Returns the name of the Python executable for this OS
* @returns The Python executable name for the current platform
Expand Down Expand Up @@ -336,10 +354,33 @@ export class InitTemplate {
throw new ToolkitError(`Template path does not exist: ${basePath}`);
}

const languages = await getLanguageDirectories(basePath);
let actualBasePath = basePath;
let languages = await getLanguageDirectories(basePath);

// Auto-detect single language templates
if (languages.length === 0) {
const entries = await fs.readdir(basePath, { withFileTypes: true });
Copy link
Contributor

Choose a reason for hiding this comment

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

We are performing fs.readDir here twice with the same parameters. getLanguageDirectories calls it in the same way. Try to reuse the result instead of calling it again so soon. See

async function getLanguageDirectories(templatePath: string): Promise<string[]> {
.

Copy link
Contributor

Choose a reason for hiding this comment

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

Changed the return signature of getLanguageDirectories to include entries, so that we can reuse it here and speed up the code.

const languageDirs = entries.filter(entry =>
entry.isDirectory() &&
['typescript', 'javascript', 'python', 'java', 'csharp', 'fsharp', 'go'].includes(entry.name),
Copy link
Contributor

Choose a reason for hiding this comment

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

No need to name these, iterate the keys from languageExtensions earlier in the file.

Copy link
Contributor

Choose a reason for hiding this comment

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

Addressed this by implementing my suggestion. Moved language extensions to a new interface LanguageInfo.

);

if (languageDirs.length === 1) {
// Validate that the language directory contains appropriate files
const langDir = languageDirs[0].name;
const langDirPath = path.join(basePath, langDir);
const hasValidFiles = await hasLanguageFiles(langDirPath, getLanguageExtensions(langDir));

if (hasValidFiles) {
actualBasePath = path.join(basePath, langDir);
Copy link
Contributor

Choose a reason for hiding this comment

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

Two variables here have the same value—as long as hasLanguageFiles is a pure function (which it should be, if it's not, it should be corrected).

langDirPath and actualBasePath.

Copy link
Contributor

Choose a reason for hiding this comment

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

Addressed this by consolidating the two variables.

languages = [langDir];
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like a silent failure if hasValidFiles is falsy. Shouldn't we fail or at least log a warning in this case?

Copy link
Contributor

Choose a reason for hiding this comment

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

Removed dead code.

}
}
}

const name = path.basename(basePath);

return new InitTemplate(basePath, name, languages, null, TemplateType.CUSTOM);
return new InitTemplate(actualBasePath, name, languages, null, TemplateType.CUSTOM);
}

public readonly description?: string;
Expand Down Expand Up @@ -400,7 +441,13 @@ export class InitTemplate {
projectInfo.versions['aws-cdk-lib'] = libVersion;
}

const sourceDirectory = path.join(this.basePath, language);
let sourceDirectory = path.join(this.basePath, language);

// For auto-detected single language templates, use basePath directly
if (this.templateType === TemplateType.CUSTOM && this.languages.length === 1 &&
path.basename(this.basePath) === language) {
sourceDirectory = this.basePath;
}

if (this.templateType === TemplateType.CUSTOM) {
// For custom templates, copy files without processing placeholders
Expand Down
Loading