From 1b801dc021f2ceacba786ca9887c904b0e0b57e7 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Wed, 6 Sep 2023 15:31:46 -0300 Subject: [PATCH] Task/support bs const in bsconfig (#887) * First pass at adding bs_const support to the bs config * Lint fixes * Added a test for if there was no bs_consts in the manifest * Apply suggestions from code review Co-authored-by: Christopher Dwyer-Perkins --------- Co-authored-by: Bronley Plumb --- bsconfig.schema.json | 15 ++++++ src/BsConfig.ts | 4 ++ src/Program.spec.ts | 88 ++++++++++++++++++++++++++++++++++++ src/Program.ts | 28 +++++++++++- src/preprocessor/Manifest.ts | 4 +- 5 files changed, 135 insertions(+), 4 deletions(-) diff --git a/bsconfig.schema.json b/bsconfig.schema.json index 5e8f6436a..e7b071a8b 100644 --- a/bsconfig.schema.json +++ b/bsconfig.schema.json @@ -21,6 +21,21 @@ "description": "The root directory of your Roku project. Defaults to the current working directory (or cwd property)", "type": "string" }, + "manifest": { + "description": "A entry to modify manifest values", + "type": "object", + "properties": { + "bs_const": { + "description": "A dictionary of bs_consts to change or add to the manifest. Each entry ", + "type": "object", + "patternProperties": { + "^.+$": { + "type": ["boolean", "null"] + } + } + } + } + }, "files": { "description": "The list of files that should be used in this project. Supports globs. Optionally, you can specify an object with `src` and `dest` properties to move files from one location into a different destination location", "default": [ diff --git a/src/BsConfig.ts b/src/BsConfig.ts index 8dc633e7a..941761259 100644 --- a/src/BsConfig.ts +++ b/src/BsConfig.ts @@ -12,6 +12,10 @@ export interface BsConfig { */ project?: string; + manifest?: { + bs_const?: Record; + }; + /** * Relative or absolute path to another bsconfig.json file that this file should import and then override. * Prefix with a question mark (?) to prevent throwing an exception if the file does not exist. diff --git a/src/Program.spec.ts b/src/Program.spec.ts index 7e559757e..5367495cf 100644 --- a/src/Program.spec.ts +++ b/src/Program.spec.ts @@ -2966,4 +2966,92 @@ describe('Program', () => { ]); }); }); + + describe('getManifest', () => { + beforeEach(() => { + fsExtra.emptyDirSync(tempDir); + fsExtra.writeFileSync(`${tempDir}/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.options = { + rootDir: tempDir + }; + }); + + afterEach(() => { + fsExtra.emptyDirSync(tempDir); + program.dispose(); + }); + + it('loads the manifest', () => { + let manifest = program.getManifest(); + testCommonManifestValues(manifest); + expect(manifest.get('bs_const')).to.equal('DEBUG=false'); + }); + + it('adds a const to the manifest', () => { + program.options.manifest = { + // eslint-disable-next-line camelcase + bs_const: { + NEW_VALUE: false + } + }; + let manifest = program.getManifest(); + testCommonManifestValues(manifest); + expect(manifest.get('bs_const')).to.equal('DEBUG=false;NEW_VALUE=false'); + }); + + it('changes a const in the manifest', () => { + program.options.manifest = { + // eslint-disable-next-line camelcase + bs_const: { + DEBUG: true + } + }; + let manifest = program.getManifest(); + testCommonManifestValues(manifest); + expect(manifest.get('bs_const')).to.equal('DEBUG=true'); + }); + + it('removes a const in the manifest', () => { + program.options.manifest = { + // eslint-disable-next-line camelcase + bs_const: { + DEBUG: null + } + }; + let manifest = program.getManifest(); + testCommonManifestValues(manifest); + expect(manifest.get('bs_const')).to.equal(''); + }); + + it('handles no consts in the manifest', () => { + fsExtra.emptyDirSync(tempDir); + fsExtra.writeFileSync(`${tempDir}/manifest`, trim` + # Channel Details + title=sample manifest + major_version=2 + minor_version=0 + build_version=0 + supports_input_launch=1 + `); + let manifest = program.getManifest(); + testCommonManifestValues(manifest); + expect(manifest.get('bs_const')).to.equal(''); + }); + + function testCommonManifestValues(manifest: Map) { + expect(manifest.get('title')).to.equal('sample manifest'); + expect(manifest.get('major_version')).to.equal('2'); + expect(manifest.get('minor_version')).to.equal('0'); + expect(manifest.get('build_version')).to.equal('0'); + expect(manifest.get('supports_input_launch')).to.equal('1'); + } + }); }); diff --git a/src/Program.ts b/src/Program.ts index 6eb765688..066b17285 100644 --- a/src/Program.ts +++ b/src/Program.ts @@ -16,7 +16,7 @@ import { DependencyGraph } from './DependencyGraph'; import { Logger, LogLevel } from './Logger'; import chalk from 'chalk'; import { globalFile } from './globalCallables'; -import { parseManifest } from './preprocessor/Manifest'; +import { parseManifest, getBsConst } from './preprocessor/Manifest'; import { URI } from 'vscode-uri'; import PluginInterface from './PluginInterface'; import { isBrsFile, isXmlFile, isXmlScope, isNamespaceStatement } from './astUtils/reflection'; @@ -1362,7 +1362,31 @@ export class Program { try { //we only load this manifest once, so do it sync to improve speed downstream contents = fsExtra.readFileSync(manifestPath, 'utf-8'); - this._manifest = parseManifest(contents); + 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); + } + } + + // 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); + + this._manifest = parsedManifest; } catch (err) { this._manifest = new Map(); } diff --git a/src/preprocessor/Manifest.ts b/src/preprocessor/Manifest.ts index 418ece8a1..03cb0d2a2 100644 --- a/src/preprocessor/Manifest.ts +++ b/src/preprocessor/Manifest.ts @@ -61,7 +61,7 @@ export function parseManifest(contents: string) { * @returns a map of key to boolean value representing the `bs_const` attribute, or an empty map if * no `bs_const` attribute is found. */ -export function getBsConst(manifest: Manifest): Map { +export function getBsConst(manifest: Manifest, toLowerKeys = true): Map { if (!manifest.has('bs_const')) { return new Map(); } @@ -89,7 +89,7 @@ export function getBsConst(manifest: Manifest): Map { return [keyValuePair.slice(0, equals), keyValuePair.slice(equals + 1)]; }) // remove leading/trailing whitespace from keys and values, and force everything to lower case - .map(([key, value]) => [key.trim().toLowerCase(), value.trim().toLowerCase()]) + .map(([key, value]) => [toLowerKeys ? key.trim().toLowerCase() : key.trim(), value.trim().toLowerCase()]) // convert value to boolean or throw .map(([key, value]): [string, boolean] => { if (value.toLowerCase() === 'true') {