Skip to content
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
25 changes: 23 additions & 2 deletions src/Program.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2967,7 +2967,7 @@ describe('Program', () => {
});
});

describe('getManifest', () => {
describe('manifest', () => {
beforeEach(() => {
fsExtra.emptyDirSync(tempDir);
fsExtra.writeFileSync(`${tempDir}/manifest`, trim`
Expand All @@ -2989,7 +2989,28 @@ describe('Program', () => {
program.dispose();
});

it('loads the manifest', () => {
it('loads the manifest from project root', () => {
let manifest = program.getManifest();
testCommonManifestValues(manifest);
expect(manifest.get('bs_const')).to.equal('DEBUG=false');
});

it('loads the manifest from a FileObj', () => {
fsExtra.emptyDirSync(tempDir);
fsExtra.ensureDirSync(`${tempDir}/someDeepDir`);
fsExtra.writeFileSync(`${tempDir}/someDeepDir/manifest`, trim`
# Channel Details
title=sample manifest
major_version=2
minor_version=0
build_version=0
supports_input_launch=1
bs_const=DEBUG=false
`);
program.loadManifest({
src: `${tempDir}/someDeepDir/manifest`,
dest: 'manifest'
});
let manifest = program.getManifest();
testCommonManifestValues(manifest);
expect(manifest.get('bs_const')).to.equal('DEBUG=false');
Expand Down
88 changes: 51 additions & 37 deletions src/Program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1349,51 +1349,65 @@ export class Program {
return files;
}

private _manifest: Map<string, string>;

/**
* Get a map of the manifest information
* Modify a parsed manifest map by reading `bs_const` and injecting values from `options.manifest.bs_const`
* @param parsedManifest The manifest map to read from and modify
*/
public getManifest() {
if (!this._manifest) {
//load the manifest file.
//TODO update this to get the manifest from the files array or require it in the options...we shouldn't assume the location of the manifest
let manifestPath = path.join(this.options.rootDir, 'manifest');

let contents: string;
try {
//we only load this manifest once, so do it sync to improve speed downstream
contents = fsExtra.readFileSync(manifestPath, 'utf-8');
let parsedManifest = parseManifest(contents);

// Lift the bs_consts defined in the manifest
let bsConsts = getBsConst(parsedManifest, false);

// Override or delete any bs_consts defined in the bs config
for (const key in this.options?.manifest?.bs_const) {
const value = this.options.manifest.bs_const[key];
if (value === null) {
bsConsts.delete(key);
} else {
bsConsts.set(key, value);
}
}
private buildBsConstsIntoParsedManifest(parsedManifest: Map<string, string>) {
// Lift the bs_consts defined in the manifest
let bsConsts = getBsConst(parsedManifest, false);

// Override or delete any bs_consts defined in the bs config
for (const key in this.options?.manifest?.bs_const) {
const value = this.options.manifest.bs_const[key];
if (value === null) {
bsConsts.delete(key);
} else {
bsConsts.set(key, value);
}
}

// convert the new list of bs consts back into a string for the rest of the down stream systems to use
let constString = '';
for (const [key, value] of bsConsts) {
constString += `${constString !== '' ? ';' : ''}${key}=${value.toString()}`;
}
// convert the new list of bs consts back into a string for the rest of the down stream systems to use
let constString = '';
for (const [key, value] of bsConsts) {
constString += `${constString !== '' ? ';' : ''}${key}=${value.toString()}`;
}

// Set the updated bs_const value
parsedManifest.set('bs_const', constString);
// Set the updated bs_const value
parsedManifest.set('bs_const', constString);
}

this._manifest = parsedManifest;
} catch (err) {
this._manifest = new Map();
}
/**
* Try to find and load the manifest into memory
* @param manifestFileObj A pointer to a potential manifest file object found during loading
*/
public loadManifest(manifestFileObj?: FileObj) {
let manifestPath = manifestFileObj
? manifestFileObj.src
: path.join(this.options.rootDir, 'manifest');

try {
// we only load this manifest once, so do it sync to improve speed downstream
const contents = fsExtra.readFileSync(manifestPath, 'utf-8');
const parsedManifest = parseManifest(contents);
this.buildBsConstsIntoParsedManifest(parsedManifest);
this._manifest = parsedManifest;
} catch (e) {
this._manifest = new Map();
}
}

/**
* Get a map of the manifest information
*/
public getManifest() {
if (!this._manifest) {
this.loadManifest();
}
return this._manifest;
}
private _manifest: Map<string, string>;

public dispose() {
this.plugins.emit('beforeProgramDispose', { program: this });
Expand Down
22 changes: 22 additions & 0 deletions src/ProgramBuilder.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,28 @@ describe('ProgramBuilder', () => {
expect(stub.getCalls()).to.be.lengthOf(3);
});

it('finds and loads a manifest before all other files', async () => {
sinon.stub(util, 'getFilePaths').returns(Promise.resolve([{
src: 'file1.brs',
dest: 'file1.brs'
}, {
src: 'file2.bs',
dest: 'file2.bs'
}, {
src: 'file3.xml',
dest: 'file4.xml'
}, {
src: 'manifest',
dest: 'manifest'
}]));

let stubLoadManifest = sinon.stub(builder.program, 'loadManifest');
let stubSetFile = sinon.stub(builder.program, 'setFile');
sinon.stub(builder, 'getFileContents').returns(Promise.resolve(''));
await builder['loadAllFilesAST']();
expect(stubLoadManifest.calledBefore(stubSetFile)).to.be.true;
});

it('loads all type definitions first', async () => {
const requestedFiles = [] as string[];
builder['fileResolvers'].push((filePath) => {
Expand Down
61 changes: 22 additions & 39 deletions src/ProgramBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,59 +466,42 @@ export class ProgramBuilder {
*/
private async loadAllFilesAST() {
await this.logger.time(LogLevel.log, ['Parsing files'], async () => {
let errorCount = 0;
let files = await this.logger.time(LogLevel.debug, ['getFilePaths'], async () => {
return util.getFilePaths(this.options);
});
this.logger.trace('ProgramBuilder.loadAllFilesAST() files:', files);

const acceptableSourceExtensions = ['.bs', '.brs', '.xml'];
const typedefFiles = [] as FileObj[];
const nonTypedefFiles = [] as FileObj[];
const sourceFiles = [] as FileObj[];
let manifestFile: FileObj | null = null;

for (const file of files) {
const srcLower = file.src.toLowerCase();
if (srcLower.endsWith('.d.bs')) {
typedefFiles.push(file);
} else if (acceptableSourceExtensions.includes(path.extname(srcLower))) {
sourceFiles.push(file);
} else {
nonTypedefFiles.push(file);
if (file.dest.toLowerCase() === 'manifest') {
manifestFile = file;
}
}
}

//preload every type definition file first, which eliminates duplicate file loading
await Promise.all(
typedefFiles.map(async (fileObj) => {
try {
this.program.setFile(
fileObj,
await this.getFileContents(fileObj.src)
);
} catch (e) {
//log the error, but don't fail this process because the file might be fixable later
this.logger.log(e);
}
})
);

const acceptableExtensions = ['.bs', '.brs', '.xml'];
//parse every file other than the type definitions
await Promise.all(
nonTypedefFiles.map(async (fileObj) => {
try {
let fileExtension = path.extname(fileObj.src).toLowerCase();

//only process certain file types
if (acceptableExtensions.includes(fileExtension)) {
this.program.setFile(
fileObj,
await this.getFileContents(fileObj.src)
);
}
} catch (e) {
//log the error, but don't fail this process because the file might be fixable later
this.logger.log(e);
}
})
);
return errorCount;
if (manifestFile) {
this.program.loadManifest(manifestFile);
}

const loadFile = async (fileObj) => {
try {
this.program.setFile(fileObj, await this.getFileContents(fileObj.src));
} catch (e) {
this.logger.log(e); // log the error, but don't fail this process because the file might be fixable later
}
};
await Promise.all(typedefFiles.map(loadFile)); // preload every type definition file, which eliminates duplicate file loading
await Promise.all(sourceFiles.map(loadFile)); // parse source files
});
}

Expand Down