Skip to content

Commit

Permalink
Merge pull request #8 from rokucommunity/fix-xml-import
Browse files Browse the repository at this point in the history
Fix missing xml import for newly-generated codebehind file
  • Loading branch information
TwitchBronBron authored Jan 8, 2025
2 parents 4864375 + 1651348 commit ad5458c
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 23 deletions.
2 changes: 1 addition & 1 deletion src/Plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ export class Plugin implements CompilerPlugin {
program.removeFile(file.pkgPath);
}
}
}
}
52 changes: 44 additions & 8 deletions src/findNodes.spec.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
import { Program, util } from 'brighterscript';
import { Program, util, standardizePath as s } from 'brighterscript';
import { expect } from 'chai';
import { Plugin } from './Plugin';
import * as path from 'path';
import undent from 'undent';
import * as fsExtra from 'fs-extra';
const tempDir = s`${__dirname}/../.tmp`;
const rootDir = s`${tempDir}/rootDir`;
const stagingDir = s`${tempDir}/stagingDir`;

describe('findnode', () => {
let program: Program;
const rootDir = path.join(__dirname, '../.tmp');

beforeEach(() => {
program = new Program({ rootDir: rootDir });
fsExtra.emptyDirSync(tempDir);
fsExtra.emptyDirSync(rootDir);
fsExtra.emptyDirSync(stagingDir);

program = new Program({
rootDir: rootDir,
stagingDir: stagingDir
});
program.plugins.add(new Plugin());
});

afterEach(() => {
fsExtra.removeSync(tempDir);
});

it('it works when a bs file is present', async () => {
program.setFile('components/ZombieKeyboard.bs', `
sub init()
Expand Down Expand Up @@ -40,19 +54,38 @@ describe('findnode', () => {

it('it works when no bs file is present', async () => {
program.setFile('components/ZombieKeyboard.xml', `
<component name="ZombieKeyboard">
<component name="ZombieKeyboard" extends="group">
<children>
<label id="helloZombieText" />
</children>
</component>
`);

const result = await program.getTranspiledFileContents('components/ZombieKeyboard.bs');
expect(result.code).to.equal(undent`
//for this test, we need to actually run a full build because this file won't exist until after the build
program.validate();
expect(program.getDiagnostics().map(x => x.message)).to.eql([]);
await program.transpile([], stagingDir);

expect(
fsExtra.readFileSync(s`${stagingDir}/components/ZombieKeyboard.brs`).toString()
).to.equal(undent`
sub init()
m.helloZombieText = m.top.findNode("helloZombieText")
end sub
`);

//make sure the import to this new file is present in the xml file
expect(
undent(fsExtra.readFileSync(s`${stagingDir}/components/ZombieKeyboard.xml`).toString())
).to.equal(undent`
<component name="ZombieKeyboard" extends="group">
<script uri="pkg:/components/ZombieKeyboard.brs" type="text/brightscript" />
<script type="text/brightscript" uri="pkg:/source/bslib.brs" />
<children>
<label id="helloZombieText" />
</children>
</component>
`);
});

it('it works when an empty file is present', async () => {
Expand All @@ -61,6 +94,7 @@ describe('findnode', () => {

program.setFile('components/ZombieKeyboard.xml', `
<component name="ZombieKeyboard">
<script uri="ZombieKeyboard.bs" />
<children>
<label id="helloZombieText" />
</children>
Expand All @@ -75,14 +109,15 @@ describe('findnode', () => {
`);
});

it('it works when an file is present with an empty init function', async () => {
it('it works when a file is present with an empty init function', async () => {
program.setFile('components/ZombieKeyboard.bs', `
sub init()
end sub
`);

program.setFile('components/ZombieKeyboard.xml', `
<component name="ZombieKeyboard">
<script uri="ZombieKeyboard.bs" />
<children>
<label id="helloZombieText" />
</children>
Expand Down Expand Up @@ -194,7 +229,7 @@ describe('findnode', () => {

it('it works when you extend a component and founds nodes are declared within their correct component', async () => {
program.setFile('components/BaseKeyboard.xml', `
<component name="BaseKeyboard">
<component name="BaseKeyboard" extends="group">
<children>
<label id="helloText" />
</children>
Expand All @@ -209,6 +244,7 @@ describe('findnode', () => {

program.setFile('components/ZombieKeyboard.xml', `
<component name="ZombieKeyboard" extends="BaseKeyboard">
<script uri="ZombieKeyboard.bs" />
<children>
</children>
</component>
Expand Down
38 changes: 24 additions & 14 deletions src/findNodes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { AstEditor, FunctionStatement, BrsFile, Program, TranspileObj, BscFile, Statement, Range } from 'brighterscript';
import { isBrsFile, Parser, isXmlScope, DiagnosticSeverity, createVisitor, WalkMode, isDottedGetExpression, isVariableExpression, isLiteralString, util } from 'brighterscript';
import type { SGNode } from 'brighterscript/dist/parser/SGTypes';
import type { AstEditor, FunctionStatement, BrsFile, Program, BscFile, Range, TranspileObj } from 'brighterscript';
import { isBrsFile, Parser, isXmlScope, DiagnosticSeverity, createVisitor, WalkMode, isDottedGetExpression, isVariableExpression, isLiteralString, util, createSGAttribute } from 'brighterscript';
import { SGScript, type SGNode } from 'brighterscript/dist/parser/SGTypes';

function findChildrenWithIDs(children: Array<SGNode>): Map<string, Range> {
let foundIDs = new Map<string, Range>();
Expand All @@ -27,39 +27,49 @@ export function findNodeWithIDInjection(program: Program, entries: TranspileObj[
//find an init function from all the scope's files
let initFunction: FunctionStatement | undefined;

let hasBrsFile = false;
let brsFileWithInit: BrsFile | undefined;
for (const file of scopeFiles) {
if (isBrsFile(file)) {
hasBrsFile = true;
initFunction = file.parser.references.functionStatementLookup.get('init');
if (initFunction) {
brsFileWithInit = file;
break;
}
}
}

if (!hasBrsFile) {
createdFiles.push(program.setFile(xmlFile.pkgPath.replace('.xml', '.bs'), ''));
//if we don't have any brs files with an init, then we need to make a new BrsFile that we can add the `init()` function to
if (!brsFileWithInit) {
brsFileWithInit = program.setFile<BrsFile>(xmlFile.pkgPath.replace('.xml', '.bs'), '');
createdFiles.push(brsFileWithInit);

//add this import to the xml file
editor.arrayPush(xmlFile.parser.ast.component!.scripts, new SGScript({
text: 'script'
}, [
createSGAttribute('uri', util.sanitizePkgPath(brsFileWithInit.pkgPath))
]));
}

//create an init function if it's missing
if (!initFunction) {
const codeBehindFile = program.getFiles<BrsFile>(xmlFile.possibleCodebehindPkgPaths).find(x => !!x);
initFunction = Parser.parse(`sub init()\nend sub`).statements[0] as FunctionStatement;
if (codeBehindFile) {
editor.arrayPush(codeBehindFile.parser.statements, initFunction);
brsFileWithInit = program.getFiles<BrsFile>(xmlFile.possibleCodebehindPkgPaths).find(x => !!x);
initFunction = Parser.parse(`sub init()\nend sub`).ast.statements[0] as FunctionStatement;
if (brsFileWithInit) {
editor.arrayPush(brsFileWithInit.parser.ast.statements, initFunction);
}
}

if (initFunction) {
if (brsFileWithInit && initFunction) {
//add m variables for every xml component that has an id
// eslint-disable-next-line max-statements-per-line, @typescript-eslint/brace-style
const assignments = Array.from(ids).map(([id, range]) => { return `m.${id} = m.top.findNode("${id}")`; }).join('\n');
const statements = (Parser.parse(`
const parser = Parser.parse(`
sub temp()
${assignments}
end sub
`).statements[0] as FunctionStatement).func.body.statements;
`);
const statements = (parser.ast.statements[0] as FunctionStatement).func.body.statements;
//add the assignments to the top of the init function
editor.arrayUnshift(initFunction.func.body.statements, ...statements);
}
Expand Down

0 comments on commit ad5458c

Please sign in to comment.