Skip to content

Commit

Permalink
Merge pull request #1858 from Windvis/macros/macroCondition-unless-ke…
Browse files Browse the repository at this point in the history
…yword-support

Add support for `{{unless}}` to the `macroCondition` macro
  • Loading branch information
mansona committed Apr 17, 2024
2 parents 284191d + 6d2baff commit a10ef00
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 1 deletion.
28 changes: 27 additions & 1 deletion packages/macros/src/glimmer/ast-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ import literal from './literal';
import getConfig from './get-config';
import dependencySatisfies from './dependency-satisfies';
import { maybeAttrs } from './macro-maybe-attrs';
import { macroIfBlock, macroIfExpression, macroIfMustache } from './macro-condition';
import {
macroIfBlock,
macroIfExpression,
macroIfMustache,
macroUnlessBlock,
macroUnlessExpression,
macroUnlessMustache,
} from './macro-condition';
import { failBuild } from './fail-build';
import { RewrittenPackageCache } from '@embroider/shared-internals';

Expand Down Expand Up @@ -181,6 +188,9 @@ export function makeSecondTransform() {
if (node.path.original === 'if') {
return macroIfBlock(node);
}
if (node.path.original === 'unless') {
return macroUnlessBlock(node);
}
},
SubExpression(node: any) {
if (node.path.type !== 'PathExpression') {
Expand All @@ -192,6 +202,9 @@ export function makeSecondTransform() {
if (node.path.original === 'if') {
return macroIfExpression(node, env.syntax.builders);
}
if (node.path.original === 'unless') {
return macroUnlessExpression(node, env.syntax.builders);
}
if (node.path.original === 'macroFailBuild') {
failBuild(node);
}
Expand All @@ -208,6 +221,16 @@ export function makeSecondTransform() {
return false;
}
}
if (
modifier.path.type === 'SubExpression' &&
modifier.path.path.type === 'PathExpression' &&
modifier.path.path.original === 'unless'
) {
modifier.path = macroUnlessExpression(modifier.path, env.syntax.builders);
if (modifier.path.type === 'UndefinedLiteral') {
return true;
}
}
if (modifier.path.type !== 'PathExpression') {
return true;
}
Expand All @@ -231,6 +254,9 @@ export function makeSecondTransform() {
if (node.path.original === 'if') {
return macroIfMustache(node, env.syntax.builders);
}
if (node.path.original === 'unless') {
return macroUnlessMustache(node, env.syntax.builders);
}
if (node.path.original === 'macroFailBuild') {
failBuild(node);
}
Expand Down
64 changes: 64 additions & 0 deletions packages/macros/src/glimmer/macro-condition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,67 @@ export function macroIfMustache(node: any, builders: any) {

return builders.mustache(result);
}

export function macroUnlessBlock(node: any) {
let condition = node.params[0];

if (!condition || condition.type !== 'SubExpression' || condition.path.original !== 'macroCondition') {
return node;
}

if (condition.params.length !== 1) {
throw new Error(`macroCondition requires one arguments, you passed ${node.params.length}`);
}

let result = evaluate(condition.params[0]);
if (!result.confident) {
throw new Error(`argument to macroCondition must be statically analyzable`);
}

if (result.value) {
if (node.inverse) {
return node.inverse.body;
} else {
return [];
}
} else {
return node.program.body;
}
}

export function macroUnlessExpression(node: any, builders: any) {
let condition = node.params[0];

if (!condition || condition.type !== 'SubExpression' || condition.path.original !== 'macroCondition') {
return node;
}

if (condition.params.length !== 1) {
throw new Error(`macroCondition requires one arguments, you passed ${node.params.length}`);
}

let result = evaluate(condition.params[0]);
if (!result.confident) {
throw new Error(`argument to macroCondition must be statically analyzable`);
}

if (result.value) {
return node.params[2] || builders.undefined();
} else {
return node.params[1];
}
}

export function macroUnlessMustache(node: any, builders: any) {
let result = macroUnlessExpression(node, builders);

if (result === node) {
return node;
}

if (result.type === 'SubExpression') {
return builders.mustache(result.path, result.params, result.hash);
}

return builders.mustache(result);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, click } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { helper } from '@ember/component/helper';

module('Integration | Macro | macroCondition + {{unless}}', function (hooks) {
setupRenderingTest(hooks);

test('macroCondition in content position when true', async function (assert) {
await render(hbs`{{#unless (macroCondition true)}}red{{else}}blue{{/unless}}`);
assert.equal(this.element.textContent.trim(), 'blue');
});

test('macroCondition in content position when false', async function (assert) {
await render(hbs`{{#unless (macroCondition false)}}red{{else}}blue{{/unless}}`);
assert.equal(this.element.textContent.trim(), 'red');
});

test('macroCondition in content position when true with no alternate', async function (assert) {
await render(hbs`{{#unless (macroCondition true)}}red{{/unless}}`);
assert.equal(this.element.textContent.trim(), '');
});

test('macroCondition in subexpression position when true', async function (assert) {
assert.expect(1);
this.owner.register(
'helper:my-assertion',
helper(function ([value]) {
assert.strictEqual(value, 'blue');
})
);
await render(hbs`{{my-assertion (unless (macroCondition true) 'red' 'blue') }}`);
});

test('macroCondition inside string', async function (assert) {
assert.expect(1);
await render(hbs`<div class="target {{unless (macroCondition true) 'red' 'blue' }}"></div>`);
assert.ok(this.element.querySelector('.target').matches('.blue'));
});

test('macroCondition in subexpression position when false', async function (assert) {
assert.expect(1);
this.owner.register(
'helper:my-assertion',
helper(function ([value]) {
assert.strictEqual(value, 'red');
})
);
await render(hbs`{{my-assertion (unless (macroCondition false) 'red' 'blue') }}`);
});

test('macroCondition in subexpression position when true with no alternate', async function (assert) {
assert.expect(1);
this.owner.register(
'helper:my-assertion',
helper(function ([value]) {
assert.strictEqual(value, undefined);
})
);
await render(hbs`{{my-assertion (unless (macroCondition true) 'red') }}`);
});

test('macroCondition composes with other macros, true case', async function (assert) {
assert.expect(1);
this.owner.register(
'helper:my-assertion',
helper(function ([value]) {
assert.strictEqual(value, 'blue');
})
);
await render(
hbs`{{my-assertion (unless (macroCondition (macroDependencySatisfies 'ember-source' '*')) 'red' 'blue') }}`
);
});

test('macroCondition composes with other macros, false case', async function (assert) {
assert.expect(1);
this.owner.register(
'helper:my-assertion',
helper(function ([value]) {
assert.strictEqual(value, 'red');
})
);
await render(
hbs`{{my-assertion (unless (macroCondition (macroDependencySatisfies 'ember-source' '10.x')) 'red' 'blue') }}`
);
});

test('macroCondition composes with self', async function (assert) {
assert.expect(1);
this.owner.register(
'helper:my-assertion',
helper(function ([value]) {
assert.strictEqual(value, 'red');
})
);
await render(hbs`{{my-assertion (unless (macroCondition false) (unless (macroCondition true) 'green' 'red') 'blue') }}`);
});

test('macroCondition in modifier position when false', async function (assert) {
assert.expect(1);
this.doThing = function () {
assert.ok(true, 'it ran');
};
await render(
hbs('<button {{(unless (macroCondition false) on) "click" this.doThing}}>Submit</button>', {
insertRuntimeErrors: true,
})
);
await click('button');
});

test('macroCondition in modifier position when true', async function (assert) {
assert.expect(1);
this.doThing = function () {
assert.ok(true, 'it ran');
};
await render(
hbs('<button {{(unless (macroCondition false) on off) "click" this.doThing}}>Submit</button>', {
insertRuntimeErrors: true,
})
);
await click('button');
});

test('macroCondition in modifier position when true with no alternate', async function (assert) {
assert.expect(0);
this.doThing = function () {
assert.ok(true, 'it ran');
};
await render(
hbs('<button {{(unless (macroCondition true) on) "click" this.doThing}}>Submit</button>', {
insertRuntimeErrors: true,
})
);
await click('button');
});
});

0 comments on commit a10ef00

Please sign in to comment.