diff --git a/__tests__/tests.ts b/__tests__/tests.ts index 86ed1fe..faedb60 100644 --- a/__tests__/tests.ts +++ b/__tests__/tests.ts @@ -33,6 +33,7 @@ describe('htmlbars-inline-precompile', function () { }); afterEach(function () { + sharedStateToBabel = null; sinon.restore(); }); @@ -654,13 +655,23 @@ describe('htmlbars-inline-precompile', function () { ); }); + let sharedStateToBabel = null as any; + let expressionTransform: ExtendedPluginBuilder = (env) => { return { name: 'expression-transform', visitor: { PathExpression(node, path) { if (node.original === 'onePlusOne') { + let boundName = sharedStateToBabel?.boundName; + if (boundName) { + env.meta.jsutils.bindVariable(boundName); + return env.syntax.builders.path(boundName); + } let name = env.meta.jsutils.bindExpression('1+1', path, { nameHint: 'two' }); + if (sharedStateToBabel) { + sharedStateToBabel.boundName = name; + } return env.syntax.builders.path(name); } return undefined; @@ -1314,6 +1325,47 @@ describe('htmlbars-inline-precompile', function () { `); }); + it('can reuse bindings', function () { + plugins = [ + [HTMLBarsInlinePrecompile, { targetFormat: 'hbs', transforms: [expressionTransform] }], + ]; + + sharedStateToBabel = {}; + + let transformed = transform(stripIndent` + import { precompileTemplate } from '@ember/template-compilation'; + import Message from 'message'; + const template = precompileTemplate('', { + scope: () => ({ + Message + }) + }); + const template2 = precompileTemplate('', { + scope: () => ({ + Message + }) + }); + `); + + expect(transformed).toEqualCode(` + import { precompileTemplate } from '@ember/template-compilation'; + import Message from 'message'; + let two = 1 + 1; + const template = precompileTemplate("", { + scope: () => ({ + Message, + two + }) + }); + const template2 = precompileTemplate("", { + scope: () => ({ + Message, + two + }) + }); + `); + }); + it('adds new locals to preexisting renamed scope', function () { plugins = [ [HTMLBarsInlinePrecompile, { targetFormat: 'hbs', transforms: [expressionTransform] }], diff --git a/src/js-utils.ts b/src/js-utils.ts index f2468da..f72e5df 100644 --- a/src/js-utils.ts +++ b/src/js-utils.ts @@ -16,7 +16,7 @@ export class JSUtils { #babel: typeof Babel; #state: State; #template: NodePath; - #addedBinding: (name: string) => void; + #addedBinding: (name: string, jsName?: string) => void; #importer: ImportUtil; constructor( @@ -84,6 +84,20 @@ export class JSUtils { return name; } + /** + * Allows (re)use of binding that you already have in the js code or created with bindExpression + * Especially useful if you have a custom transform that creates a binding that you want to reuse + * + * @param hbsName the name you are using in your template to access the binding + * @param jsName the name of a js variable + * + * @return The name you can use in your template to access the binding. + */ + bindVariable(hbsName: string, jsName?: string) { + this.#addedBinding(hbsName, jsName); + return hbsName; + } + #emitStatement(statement: T): NodePath { if (this.#state.lastInsertedPath) { this.#state.lastInsertedPath = this.#state.lastInsertedPath.insertAfter(statement)[0];