11import path from "path" ;
2+ import { parse as yamlParse } from "yaml" ;
23import { Rule } from "../rule.js" ;
34import { RuleResult } from "../rule-result.js" ;
45import { TsvHost } from "../tsv-host.js" ;
@@ -10,8 +11,8 @@ export class FolderStructureRule implements Rule {
1011 let success = true ;
1112 let stdOutput = "" ;
1213 let errorOutput = "" ;
13- let gitRoot = host . normalizePath ( await host . gitOperation ( folder ) . revparse ( "--show-toplevel" ) ) ;
14- let relativePath = path . relative ( gitRoot , folder ) . split ( path . sep ) . join ( "/" ) ;
14+ const gitRoot = host . normalizePath ( await host . gitOperation ( folder ) . revparse ( "--show-toplevel" ) ) ;
15+ const relativePath = path . relative ( gitRoot , folder ) . split ( path . sep ) . join ( "/" ) ;
1516
1617 stdOutput += `folder: ${ folder } \n` ;
1718 if ( ! ( await host . checkFileExists ( folder ) ) ) {
@@ -32,13 +33,13 @@ export class FolderStructureRule implements Rule {
3233 } ) ;
3334
3435 // Verify top level folder is lower case and remove empty entries when splitting by slash
35- let folderStruct = relativePath . split ( "/" ) . filter ( Boolean ) ;
36+ const folderStruct = relativePath . split ( "/" ) . filter ( Boolean ) ;
3637 if ( folderStruct [ 1 ] . match ( / [ A - Z ] / g) ) {
3738 success = false ;
3839 errorOutput += `Invalid folder name. Folders under specification/ must be lower case.\n` ;
3940 }
4041
41- let packageFolder = folderStruct [ folderStruct . length - 1 ] ;
42+ const packageFolder = folderStruct [ folderStruct . length - 1 ] ;
4243
4344 // Verify package folder is at most 3 levels deep
4445 if ( folderStruct . length > 4 ) {
@@ -61,8 +62,9 @@ export class FolderStructureRule implements Rule {
6162 }
6263
6364 // Verify tspconfig, main.tsp, examples/
64- let mainExists = await host . checkFileExists ( path . join ( folder , "main.tsp" ) ) ;
65- let clientExists = await host . checkFileExists ( path . join ( folder , "client.tsp" ) ) ;
65+ const mainExists = await host . checkFileExists ( path . join ( folder , "main.tsp" ) ) ;
66+ const clientExists = await host . checkFileExists ( path . join ( folder , "client.tsp" ) ) ;
67+ const tspConfigExists = await host . checkFileExists ( path . join ( folder , "tspconfig.yaml" ) ) ;
6668
6769 if ( ! mainExists && ! clientExists ) {
6870 errorOutput += `Invalid folder structure: Spec folder must contain main.tsp or client.tsp.` ;
@@ -74,14 +76,33 @@ export class FolderStructureRule implements Rule {
7476 success = false ;
7577 }
7678
77- if (
78- ! packageFolder . includes ( "Shared" ) &&
79- ! ( await host . checkFileExists ( path . join ( folder , "tspconfig.yaml" ) ) )
80- ) {
79+ if ( ! packageFolder . includes ( "Shared" ) && ! tspConfigExists ) {
8180 errorOutput += `Invalid folder structure: Spec folder must contain tspconfig.yaml.` ;
8281 success = false ;
8382 }
8483
84+ if ( tspConfigExists ) {
85+ const configText = await host . readTspConfig ( folder ) ;
86+ const config = yamlParse ( configText ) ;
87+ const rpFolder =
88+ config ?. options ?. [ "@azure-tools/typespec-autorest" ] ?. [ "azure-resource-provider-folder" ] ;
89+ stdOutput += `azure-resource-provider-folder: ${ JSON . stringify ( rpFolder ) } \n` ;
90+
91+ if (
92+ rpFolder ?. trim ( ) ?. endsWith ( "resource-manager" ) &&
93+ ! packageFolder . endsWith ( ".Management" )
94+ ) {
95+ errorOutput += `Invalid folder structure: TypeSpec for resource-manager specs must be in a folder ending with '.Management'` ;
96+ success = false ;
97+ } else if (
98+ ! rpFolder ?. trim ( ) ?. endsWith ( "resource-manager" ) &&
99+ packageFolder . endsWith ( ".Management" )
100+ ) {
101+ errorOutput += `Invalid folder structure: TypeSpec for data-plane specs or shared code must be in a folder NOT ending with '.Management'` ;
102+ success = false ;
103+ }
104+ }
105+
85106 return {
86107 success : success ,
87108 stdOutput : stdOutput ,
0 commit comments