From c78e5ac3d19ee58e04af90df164a2da0b9ce3763 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Wed, 6 Sep 2023 13:49:12 -0300 Subject: [PATCH 1/4] First pass at adding bs_const support to the bs config --- bsconfig.schema.json | 15 +++++++ src/BsConfig.ts | 4 ++ src/Program.spec.ts | 77 ++++++++++++++++++++++++++++++++++++ src/Program.ts | 28 ++++++++++++- src/preprocessor/Manifest.ts | 4 +- 5 files changed, 124 insertions(+), 4 deletions(-) diff --git a/bsconfig.schema.json b/bsconfig.schema.json index af9668980..3abf2d546 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": { + "^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$": { + "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 51c6ba995..5c46aa548 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 174e41896..0e524adc0 100644 --- a/src/Program.spec.ts +++ b/src/Program.spec.ts @@ -1,4 +1,5 @@ import { assert, expect } from './chai-config.spec'; +import * as path from 'path'; import * as pick from 'object.pick'; import * as sinonImport from 'sinon'; import { CompletionItemKind, Position, Range } from 'vscode-languageserver'; @@ -2897,4 +2898,80 @@ describe('Program', () => { ]); }); }); + + describe.only('getManifest', () => { + const manifestPath = './test/manifest'; + beforeEach(() => { + fsExtra.ensureDirSync(tempDir); + 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.ensureDirSync(tempDir); + 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(''); + }); + + 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 4abb48b63..94f0611d7 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'; @@ -1374,7 +1374,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') { From b013848bd1e877b315d54186685b12278641a16a Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Wed, 6 Sep 2023 13:54:13 -0300 Subject: [PATCH 2/4] Lint fixes --- src/Program.spec.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Program.spec.ts b/src/Program.spec.ts index ba21e1430..5ffeb0a3d 100644 --- a/src/Program.spec.ts +++ b/src/Program.spec.ts @@ -1,5 +1,4 @@ import { assert, expect } from './chai-config.spec'; -import * as path from 'path'; import * as pick from 'object.pick'; import * as sinonImport from 'sinon'; import { CompletionItemKind, Position, Range } from 'vscode-languageserver'; @@ -2969,7 +2968,6 @@ describe('Program', () => { }); describe('getManifest', () => { - const manifestPath = './test/manifest'; beforeEach(() => { fsExtra.emptyDirSync(tempDir); fsExtra.writeFileSync(`${tempDir}/manifest`, trim` From d04e2130bb0159ed9aab664d88e21703f24b2cf5 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Wed, 6 Sep 2023 13:59:15 -0300 Subject: [PATCH 3/4] Added a test for if there was no bs_consts in the manifest --- src/Program.spec.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Program.spec.ts b/src/Program.spec.ts index 5ffeb0a3d..5367495cf 100644 --- a/src/Program.spec.ts +++ b/src/Program.spec.ts @@ -3031,6 +3031,21 @@ describe('Program', () => { 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'); From 5e0a65f2846ecd46d378aa52e865cc2dbdb7e070 Mon Sep 17 00:00:00 2001 From: Bronley Plumb Date: Wed, 6 Sep 2023 14:16:53 -0400 Subject: [PATCH 4/4] Apply suggestions from code review Co-authored-by: Christopher Dwyer-Perkins --- bsconfig.schema.json | 2 +- src/BsConfig.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bsconfig.schema.json b/bsconfig.schema.json index 2bab541d6..e7b071a8b 100644 --- a/bsconfig.schema.json +++ b/bsconfig.schema.json @@ -29,7 +29,7 @@ "description": "A dictionary of bs_consts to change or add to the manifest. Each entry ", "type": "object", "patternProperties": { - "^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,6}$": { + "^.+$": { "type": ["boolean", "null"] } } diff --git a/src/BsConfig.ts b/src/BsConfig.ts index 7f65732b8..941761259 100644 --- a/src/BsConfig.ts +++ b/src/BsConfig.ts @@ -13,7 +13,7 @@ export interface BsConfig { project?: string; manifest?: { - bs_const?: Record; + bs_const?: Record; }; /**