11const { execFileSync, spawnSync } = require ( 'child_process' ) ;
2- const { existsSync, readFileSync, statSync , writeFileSync } = require ( 'fs' ) ;
2+ const { existsSync, readdirSync , readFileSync, renameSync , statSync } = require ( 'fs' ) ;
33const { arch, platform } = require ( 'os' ) ;
4- const { dirname, join, resolve } = require ( 'path' ) ;
4+ const { dirname, format , join, parse , resolve } = require ( 'path' ) ;
55const { quote } = require ( 'shell-quote' ) ;
66
77// Terminate early if this script is not invoked with the required arguments.
@@ -186,29 +186,96 @@ try {
186186 cdsCommand = 'npx -y --package @sap/cds-dk cds' ;
187187}
188188
189+ /**
190+ * Recursively renames all .json files to .cds.json in the given directory and
191+ * its subdirectories, except for those that already have .cds.json extension.
192+ *
193+ * @param {string } dirPath - The directory path to start recursion from
194+ */
195+ function recursivelyRenameJsonFiles ( dirPath ) {
196+ // Make sure the directory exists
197+ if ( ! existsSync ( dirPath ) || ! statSync ( dirPath ) . isDirectory ( ) ) {
198+ console . log ( `Directory not found or not a directory: ${ dirPath } ` ) ;
199+ return ;
200+ }
201+ console . log ( `Processing JSON files in output directory: ${ dirPath } ` ) ;
202+ // Get all entries in the directory
203+ const entries = readdirSync ( dirPath , { withFileTypes : true } ) ;
204+ for ( const entry of entries ) {
205+ const fullPath = join ( dirPath , entry . name ) ;
206+ if ( entry . isDirectory ( ) ) {
207+ // Recursively process subdirectories
208+ recursivelyRenameJsonFiles ( fullPath ) ;
209+ } else if (
210+ entry . isFile ( ) &&
211+ entry . name . endsWith ( '.json' ) &&
212+ ! entry . name . endsWith ( '.cds.json' )
213+ ) {
214+ // Rename .json files to .cds.json
215+ const newPath = format ( { ...parse ( fullPath ) , base : '' , ext : '.cds.json' } ) ;
216+ renameSync ( fullPath , newPath ) ;
217+ console . log ( `Renamed CDS output file from ${ fullPath } to ${ newPath } ` ) ;
218+ }
219+ }
220+ }
221+
189222console . log ( 'Processing CDS files to JSON ...' ) ;
190223
191224/**
192225 * Run the cds compile command on each file in the response files list, outputting the
193226 * compiled JSON to a file with the same name but with a .json extension appended.
194227 */
195- responseFiles . forEach ( rawCdsFilePath => {
196- const cdsFilePath = quote ( [ rawCdsFilePath ] ) ;
197- const cdsJsonFilePath = `${ cdsFilePath } .json` ;
198- console . log ( `Processing CDS file ${ cdsFilePath } to ${ cdsJsonFilePath } ...` ) ;
199- const result = spawnSync (
200- cdsCommand ,
201- [
202- 'compile' , cdsFilePath ,
203- '-2' , 'json' ,
204- '--locations' ,
205- '--log-level' , 'warn'
206- ] ,
207- { shell : true , stdio : 'pipe' }
208- ) ;
209- if ( result . error || result . status !== 0 || ! result . stdout ) {
210- const errorMessage = `Could not compile the file ${ cdsFilePath } .\nReported error(s):\n\`\`\`\n${ result . stderr . toString ( ) } \n\`\`\`` ;
211- console . log ( errorMessage ) ;
228+ for ( const rawCdsFilePath of responseFiles ) {
229+ const cdsFilePath = resolve ( quote ( [ rawCdsFilePath ] ) ) ;
230+ try {
231+ if ( ! existsSync ( cdsFilePath ) ) {
232+ throw new Error ( `Expected CDS file '${ cdsFilePath } ' does not exist.` ) ;
233+ }
234+ const cdsJsonOutPath = `${ cdsFilePath } .json` ;
235+ console . log ( `Processing CDS file ${ cdsFilePath } to ${ cdsJsonOutPath } ...` ) ;
236+ const result = spawnSync (
237+ cdsCommand ,
238+ [
239+ 'compile' , cdsFilePath ,
240+ '--to' , 'json' ,
241+ '--dest' , cdsJsonOutPath ,
242+ '--locations' ,
243+ '--log-level' , 'warn'
244+ ] ,
245+ { cwd : sourceRoot , shell : true , stdio : 'pipe' }
246+ ) ;
247+ if ( result . error || result . status !== 0 ) {
248+ throw new Error (
249+ `Could not compile the file ${ cdsFilePath } .\nReported error(s):\n\`\`\`\n${ result . stderr . toString ( ) } \n\`\`\``
250+ ) ;
251+ }
252+ /**
253+ * The `cds compile` command chooses how it outputs the JSON. If it creates the
254+ * output files in a directory (at cdsJsonOutPath), then it will create the
255+ * directory when it runs and will choose the file names within that directory.
256+ * If it creates the output as a single file (at cdsJsonOutPath), then there is
257+ * nothing more to do as we create the output path by simple appending `.json` to
258+ * the input file path/name, where the input path should already end with `.cds`
259+ * (or else it shouldn't be in the response file).
260+ *
261+ * Therefore, if the output is a directory, we need to rename the output files
262+ * to have a `.cds.json` extension, not just `.json`, so that the JS extractor
263+ * recognizes them as CDS files to be indexed.
264+ */
265+ if ( ! existsSync ( cdsJsonOutPath ) || ( ! statSync ( cdsJsonOutPath ) . isFile ( ) && ! statSync ( cdsJsonOutPath ) . isDirectory ( ) ) ) {
266+ throw new Error (
267+ `CDS source file '${ cdsFilePath } ' was not compiled to JSON. This is likely because the file does not exist or is not a valid CDS file.`
268+ ) ;
269+ }
270+ if ( statSync ( cdsJsonOutPath ) . isDirectory ( ) ) {
271+ console . log ( `CDS compiler generated JSON to output directory: ${ cdsJsonOutPath } ` ) ;
272+ // Recursively rename all .json files to have a .cds.json extension
273+ recursivelyRenameJsonFiles ( cdsJsonOutPath ) ;
274+ } else {
275+ console . log ( `CDS compiler generated JSON to file: ${ cdsJsonOutPath } ` ) ;
276+ }
277+ } catch ( errorMessage ) {
278+ console . error ( `ERROR: adding diagnostic for source file=${ cdsFilePath } : ${ errorMessage } ...` ) ;
212279 try {
213280 execFileSync (
214281 codeqlExePath ,
@@ -228,12 +295,10 @@ responseFiles.forEach(rawCdsFilePath => {
228295 ) ;
229296 console . log ( `Added error diagnostic for source file: ${ cdsFilePath } ` ) ;
230297 } catch ( err ) {
231- console . error ( `Failed to add error diagnostic for source file=${ cdsFilePath } : ${ err } ` ) ;
298+ console . error ( `ERROR: Failed to add error diagnostic for source file=${ cdsFilePath } : ${ err } ` ) ;
232299 }
233300 }
234- // Write the compiled JSON result to cdsJsonFilePath.
235- writeFileSync ( cdsJsonFilePath , result . stdout ) ;
236- } ) ;
301+ }
237302
238303let excludeFilters = '' ;
239304/**
@@ -274,9 +339,6 @@ console.log(`Set $LGTM_INDEX_FILTERS to:\n${process.env.LGTM_INDEX_FILTERS}`);
274339process . env . LGTM_INDEX_TYPESCRIPT = 'NONE' ;
275340// Configure to copy over the .cds files as well, by pretending they are JSON.
276341process . env . LGTM_INDEX_FILETYPES = '.cds:JSON' ;
277- // Ignore the LGTM_INDEX_INCLUDE variable for this purpose as it may explicitly
278- // refer to .js or .ts files.
279- delete process . env . LGTM_INDEX_INCLUDE ;
280342
281343console . log (
282344 `Extracting the .cds.json files by running the 'javascript' extractor autobuild script:
0 commit comments