diff --git a/src/compileTree/compileTree.spec.ts b/src/compileTree/compileTree.spec.ts index e3a6b8b..229e8c0 100644 --- a/src/compileTree/compileTree.spec.ts +++ b/src/compileTree/compileTree.spec.ts @@ -1,5 +1,11 @@ import { CompileTree, Tree, removeHeader } from './' -import { readFile, deleteFile, fileExists } from '../' +import { + readFile, + deleteFile, + fileExists, + DependencyType, + DependencyHeader +} from '../' import path from 'path' describe('CompileTree', () => { @@ -29,6 +35,55 @@ describe('CompileTree', () => { JSON.stringify(tree, null, 2) ) }) + + it('should validate dependencies', () => { + const testDepPath = 'test.sas' + const getTestContent = (header: DependencyHeader) => `/** + @file macroWithInclude.sas + @brief test macro + + ${header} +**/ + +proc sql; +create table areas as select distinct area + from mydb.springs; + ` + + const getExpectedError = (header: DependencyHeader) => + new Error( + `Dependency '${header}' can not be used in artefact type '${DependencyType.Macro}'. Please remove it from '${testDepPath}'.` + ) + + expect(() => + compileTree['validateDependency']( + getTestContent(DependencyHeader.Include), + testDepPath, + DependencyType.Macro + ) + ).toThrow(getExpectedError(DependencyHeader.Include)) + expect(() => + compileTree['validateDependency']( + getTestContent(DependencyHeader.Binary), + testDepPath, + DependencyType.Macro + ) + ).toThrow(getExpectedError(DependencyHeader.Binary)) + + expect(() => + compileTree['validateDependency']( + getTestContent(DependencyHeader.Binary), + testDepPath + ) + ).not.toThrow() + expect(() => + compileTree['validateDependency']( + getTestContent(DependencyHeader.Binary), + testDepPath, + DependencyType.Binary + ) + ).not.toThrow() + }) }) describe('removeHeader', () => { diff --git a/src/compileTree/compileTree.ts b/src/compileTree/compileTree.ts index 7653265..9d9858c 100644 --- a/src/compileTree/compileTree.ts +++ b/src/compileTree/compileTree.ts @@ -1,7 +1,11 @@ import path from 'path' import { createFile, readFile } from '../' -import { getList, DependencyHeader, getDeprecatedHeader } from '../sasjsCli' -import { newLine } from '../formatter' +import { + getList, + DependencyHeader, + getDeprecatedHeader, + DependencyType +} from '../sasjsCli' export interface Leaf { content: string @@ -52,18 +56,54 @@ export class CompileTree { // If leaf exists, returns it's content. // Otherwise reads file, creates leaf and return content. - public async getDepContent(depPath: string) { + public async getDepContent(depPath: string, depType?: DependencyType) { const leaf = this.getLeaf(depPath) if (leaf) return leaf.content + const content = await readFile(depPath, undefined) + + this.validateDependency(content, depPath, depType) + return this.addLeaf({ - content: await readFile(depPath, undefined), + content, dependencies: [], location: depPath }).content } + // Throws an error if wrong dependencies have been used + private validateDependency( + content: string, + depPath: string, + depType?: DependencyType + ) { + if (!depType) return + + const getErrorMessage = ( + header: DependencyHeader, + dependency: DependencyType, + depPath: string + ) => + `Dependency '${header}' can not be used in artefact type '${dependency}'. Please remove it from '${depPath}'.` + + switch (depType) { + case DependencyType.Macro: + if (content.includes(DependencyHeader.Include)) { + throw new Error( + getErrorMessage(DependencyHeader.Include, depType, depPath) + ) + } else if (content.includes(DependencyHeader.Binary)) { + throw new Error( + getErrorMessage(DependencyHeader.Binary, depType, depPath) + ) + } + + default: + break + } + } + // Returns file name based on file path private getFileName(location: string) { return location.split(path.sep)[location.split(path.sep).length - 1] diff --git a/src/sasjsCli/getDependencies.ts b/src/sasjsCli/getDependencies.ts index e59fd3e..796f83a 100644 --- a/src/sasjsCli/getDependencies.ts +++ b/src/sasjsCli/getDependencies.ts @@ -104,7 +104,10 @@ export const getDependencies = async ( Object.keys(compileTree).length && depType !== DependencyType.Binary ) { - encodedFileContent = await compileTree.getDepContent(filePaths[0]) + encodedFileContent = await compileTree.getDepContent( + filePaths[0], + depType + ) } else { encodedFileContent = await readFile( filePaths[0], diff --git a/src/sasjsCli/getDependencyPaths.ts b/src/sasjsCli/getDependencyPaths.ts index 8a9f76d..5fe1af2 100644 --- a/src/sasjsCli/getDependencyPaths.ts +++ b/src/sasjsCli/getDependencyPaths.ts @@ -5,7 +5,8 @@ import { prioritiseDependencyOverrides, getList, DependencyHeader, - getDeprecatedHeader + getDeprecatedHeader, + DependencyType } from './' import { CompileTree, Leaf } from '../compileTree' @@ -19,10 +20,12 @@ export async function getDependencyPaths( let dependencyPaths: string[] = [] const foundDependencies: string[] = [] const sourcePaths = [...macroFolders, macroCorePath] + const dependenciesHeader = getDeprecatedHeader( fileContent, DependencyHeader.Macro ) + const dependencies = leaf?.dependencies || getList(dependenciesHeader, fileContent).filter((d) => d.endsWith('.sas')) @@ -38,7 +41,11 @@ export async function getDependencyPaths( let leaf: Leaf | undefined = undefined if (compileTree && Object.keys(compileTree).length) { - fileContent = await compileTree.getDepContent(filePaths[0]) + fileContent = await compileTree.getDepContent( + filePaths[0], + DependencyType.Macro + ) + leaf = compileTree.getLeaf(filePaths[0]) } else { fileContent = await readFile(filePaths[0])