diff --git a/README.md b/README.md index 615424558..bc88c86cf 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,8 @@ These are the options available in the `bsconfig.json` file. ``` - **autoImportComponentScript**: `bool` - BrighterScript only: will automatically import a script at transpile-time for a component with the same name if it exists. + - **sourceRoot*: `string` - Override the root directory path where debugger should locate the source files. The location will be embedded in the source map to help debuggers locate the original source files. This only applies to files found within rootDir. This is useful when you want to preprocess files before passing them to BrighterScript, and want a debugger to open the original files. + ## Ignore errors and warnings on a per-line basis In addition to disabling an entire class of errors in `bsconfig.json` by using `ignoreErrorCodes`, you may also disable errors for a subset of the complier rules within a file with the following comment flags: diff --git a/bsconfig.schema.json b/bsconfig.schema.json index 6374a41dc..584cb48d1 100644 --- a/bsconfig.schema.json +++ b/bsconfig.schema.json @@ -183,6 +183,10 @@ "debug", "trace" ] + }, + "sourceRoot": { + "description": "Override the root directory path where debugger should locate the source files. The location will be embedded in the source map to help debuggers locate the original source files. This only applies to files found within rootDir. This is useful when you want to preprocess files before passing them to BrighterScript, and want a debugger to open the original files.", + "type": "string" } } } \ No newline at end of file diff --git a/src/BsConfig.ts b/src/BsConfig.ts index cadc69735..930bb02f8 100644 --- a/src/BsConfig.ts +++ b/src/BsConfig.ts @@ -117,4 +117,11 @@ export interface BsConfig { * @default LogLevel.log */ logLevel?: LogLevel; + /** + * Override the path to source files in source maps. Use this if you have a preprocess step and want + * to ensure the source maps point to the original location. + * This will only alter source maps for files within rootDir. Any files found outside of rootDir will not + * have their source maps changed. + */ + sourceRoot?: string; } diff --git a/src/Program.spec.ts b/src/Program.spec.ts index 6bc92b56e..69fbb4de4 100644 --- a/src/Program.spec.ts +++ b/src/Program.spec.ts @@ -1381,4 +1381,56 @@ describe('Program', () => { expect(fsExtra.pathExistsSync(s`${stagingFolderPath}/source/bslib.brs`)).is.true; }); + describe('transpile', () => { + it('uses sourceRoot when provided for brs files', async () => { + let sourceRoot = s`${tmpPath}/sourceRootFolder`; + program = new Program({ + rootDir: rootDir, + stagingFolderPath: stagingFolderPath, + sourceRoot: sourceRoot + }); + await program.addOrReplaceFile('source/main.brs', ` + sub main() + end sub + `); + await program.transpile([{ + src: s`${rootDir}/source/main.brs`, + dest: s`source/main.brs` + }], stagingFolderPath); + + let contents = fsExtra.readFileSync(s`${stagingFolderPath}/source/main.brs.map`).toString(); + let map = JSON.parse(contents); + expect( + s`${map.sources[0]}` + ).to.eql( + s`${sourceRoot}/source/main.brs` + ); + }); + + it('uses sourceRoot when provided for bs files', async () => { + let sourceRoot = s`${tmpPath}/sourceRootFolder`; + program = new Program({ + rootDir: rootDir, + stagingFolderPath: stagingFolderPath, + sourceRoot: sourceRoot + }); + await program.addOrReplaceFile('source/main.bs', ` + sub main() + end sub + `); + await program.transpile([{ + src: s`${rootDir}/source/main.bs`, + dest: s`source/main.bs` + }], stagingFolderPath); + + let contents = fsExtra.readFileSync(s`${stagingFolderPath}/source/main.brs.map`).toString(); + let map = JSON.parse(contents); + expect( + s`${map.sources[0]}` + ).to.eql( + s`${sourceRoot}/source/main.bs` + ); + }); + }); + }); diff --git a/src/cli.ts b/src/cli.ts index ae2b963fb..f2fb92e51 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -22,6 +22,7 @@ let args = [ { name: 'root-dir', type: String, description: 'Path to the root of your project files (where the manifest lives). Defaults to current directory.' }, { name: 'staging-folder-path', type: String, description: 'The path where the files should be staged (right before being zipped up).' }, { name: 'username', type: String, description: 'The username for deploying to a Roku. Defaults to "rokudev".' }, + { name: 'source-root', type: String, description: 'Override the root directory path where debugger should locate the source files. The location will be embedded in the source map to help debuggers locate the original source files. This only applies to files found within rootDir. This is useful when you want to preprocess files before passing them to BrighterScript, and want a debugger to open the original files.' }, { name: 'watch', type: Boolean, description: 'Watch input files.' } ]; const options = commandLineArgs(args, { camelCase: true }); diff --git a/src/files/BrsFile.ts b/src/files/BrsFile.ts index a01047506..aa1f66eb8 100644 --- a/src/files/BrsFile.ts +++ b/src/files/BrsFile.ts @@ -985,15 +985,14 @@ export class BrsFile { * Convert the brightscript/brighterscript source code into valid brightscript */ public transpile() { + const state = new TranspileState(this); if (this.needsTranspiled) { - const state = new TranspileState(this); let programNode = new SourceNode(null, null, this.pathAbsolute, this.ast.transpile(state)); let result = programNode.toStringWithSourceMap({ file: this.pathAbsolute }); return result; } else { - //create a sourcemap //create a source map from the original source code let chunks = [] as (SourceNode | string)[]; let lines = this.fileContents.split(/\r?\n/g); @@ -1001,10 +1000,10 @@ export class BrsFile { let line = lines[lineIndex]; chunks.push( lineIndex > 0 ? '\n' : '', - new SourceNode(lineIndex + 1, 0, this.pathAbsolute, line) + new SourceNode(lineIndex + 1, 0, state.pathAbsolute, line) ); } - return new SourceNode(null, null, this.pathAbsolute, chunks).toStringWithSourceMap(); + return new SourceNode(null, null, state.pathAbsolute, chunks).toStringWithSourceMap(); } } diff --git a/src/parser/TranspileState.ts b/src/parser/TranspileState.ts index 8080eec59..2414740c9 100644 --- a/src/parser/TranspileState.ts +++ b/src/parser/TranspileState.ts @@ -11,23 +11,30 @@ export class TranspileState { file: BrsFile ) { this.file = file; + + //if a sourceRoot is specified, use that instead of the rootDir + if (this.file.program.options.sourceRoot) { + this.pathAbsolute = this.file.pathAbsolute.replace( + this.file.program.options.rootDir, + this.file.program.options.sourceRoot + ); + } else { + this.pathAbsolute = this.file.pathAbsolute; + } } + /** * The BrsFile that is currently being transpiled */ - file: BrsFile; - /** - * the path for this file relative to the root of the output package - */ - get pkgPath() { - return this.file.pkgPath; - } + public file: BrsFile; + /** - * the absolute path to the source location of this file + * The absolute path to the source location of this file. If sourceRoot is specified, + * this path will be full path to the file in sourceRoot instead of rootDir. + * If the file resides outside of rootDir, then no changes will be made to this path. */ - get pathAbsolute() { - return this.file.pathAbsolute; - } + public pathAbsolute: string; + /** * The number of active parent blocks for the current location of the state. */ diff --git a/src/util.ts b/src/util.ts index f4bee26bc..ae80082a7 100644 --- a/src/util.ts +++ b/src/util.ts @@ -268,6 +268,7 @@ export class Util { config.diagnosticFilters = config.diagnosticFilters ?? []; config.autoImportComponentScript = config.autoImportComponentScript === true ? true : false; config.showDiagnosticsInConsole = config.showDiagnosticsInConsole === false ? false : true; + config.sourceRoot = config.sourceRoot ? standardizePath(config.sourceRoot) : undefined; if (typeof config.logLevel === 'string') { config.logLevel = LogLevel[(config.logLevel as string).toLowerCase()]; }