Skip to content
This repository has been archived by the owner on Dec 8, 2024. It is now read-only.

Commit

Permalink
Add ability to parse scope function
Browse files Browse the repository at this point in the history
Adds the ability to parse scope functions as well as object expressions,
and deprecates parsing object expressions.
  • Loading branch information
Chris Garrett committed Mar 22, 2021
1 parent 07c1a2f commit 20fe896
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 12 deletions.
56 changes: 56 additions & 0 deletions __tests__/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,62 @@ describe('htmlbars-inline-precompile', function () {
});
});

it('correctly handles scope function', function () {
let source = 'hello';
transform(
`import { precompileTemplate } from '@ember/template-compilation';\nvar compiled = precompileTemplate('${source}', { scope: () => ({ foo, bar }) });`
);

expect(optionsReceived).toEqual({
contents: source,
locals: ['foo', 'bar'],
});
});

it('correctly handles scope function (non-block arrow function)', function () {
let source = 'hello';
transform(
`import { precompileTemplate } from '@ember/template-compilation';\nvar compiled = precompileTemplate('${source}', { scope: () => ({ foo, bar }) });`
);
expect(optionsReceived).toEqual({
contents: source,
locals: ['foo', 'bar'],
});
});

it('correctly handles scope function (block arrow function)', function () {
let source = 'hello';
transform(
`import { precompileTemplate } from '@ember/template-compilation';\nvar compiled = precompileTemplate('${source}', { scope: () => { return { foo, bar }; }});`
);
expect(optionsReceived).toEqual({
contents: source,
locals: ['foo', 'bar'],
});
});

it('correctly handles scope function (normal function)', function () {
let source = 'hello';
transform(
`import { precompileTemplate } from '@ember/template-compilation';\nvar compiled = precompileTemplate('${source}', { scope: function() { return { foo, bar }; }});`
);
expect(optionsReceived).toEqual({
contents: source,
locals: ['foo', 'bar'],
});
});

it('correctly handles scope function (object method)', function () {
let source = 'hello';
transform(
`import { precompileTemplate } from '@ember/template-compilation';\nvar compiled = precompileTemplate('${source}', { scope() { return { foo, bar }; }});`
);
expect(optionsReceived).toEqual({
contents: source,
locals: ['foo', 'bar'],
});
});

it('errors if scope contains mismatched keys/values', function () {
expect(() => {
transform(
Expand Down
55 changes: 43 additions & 12 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ module.exports = function (babel) {
`(function() {\n throw new Error('ERROR_MESSAGE');\n})();`
);

function parseExpression(buildError, name, node) {
function parseExpression(state, buildError, name, node) {
switch (node.type) {
case 'ObjectExpression':
return parseObjectExpression(buildError, name, node);
return parseObjectExpression(state, buildError, name, node);
case 'ArrayExpression': {
return parseArrayExpression(buildError, name, node);
return parseArrayExpression(state, buildError, name, node);
}
case 'StringLiteral':
case 'BooleanLiteral':
Expand All @@ -29,20 +29,50 @@ module.exports = function (babel) {
}
}

function parseArrayExpression(buildError, name, node) {
let result = node.elements.map((element) => parseExpression(buildError, name, element));
function parseArrayExpression(state, buildError, name, node) {
let result = node.elements.map((element) => parseExpression(state, buildError, name, element));

return result;
}

function parseScopeObject(buildError, name, node) {
if (node.type !== 'ObjectExpression') {
function parseScope(state, buildError, name, node) {
let body;

if (node.type === 'ObjectMethod') {
body = node.body;
} else if (node.value.type === 'ObjectExpression') {
console.warn(
`Passing an object as the \`scope\` property to inline templates has been deprecated. Please pass a function that returns an object expression instead. Usage in: ${state.file.opts.filename}`
);

body = node.value;
} else {
body = node.value.body;
}

let objExpression;

if (body && body.type === 'ObjectExpression') {
objExpression = body;
} else if (body && body.type === 'BlockStatement') {
let returnStatement = body.body[0];

if (body.body.length !== 1 || returnStatement.type !== 'ReturnStatement') {
throw new Error(
'Scope functions can only consist of a single return statement which returns an object expression containing references to in-scope values'
);
}

objExpression = returnStatement.argument;
}

if (!objExpression || objExpression.type !== 'ObjectExpression') {
throw buildError(
`Scope objects for \`${name}\` must be an object expression containing only references to in-scope values`
`Scope objects for \`${name}\` must be an object expression containing only references to in-scope values, or a function that returns an object expression containing only references to in-scope values`
);
}

return node.properties.map((prop) => {
return objExpression.properties.map((prop) => {
let { key, value } = prop;

if (value.type !== 'Identifier' || value.name !== key.name) {
Expand All @@ -55,7 +85,7 @@ module.exports = function (babel) {
});
}

function parseObjectExpression(buildError, name, node, shouldParseScope = false) {
function parseObjectExpression(state, buildError, name, node, shouldParseScope = false) {
let result = {};

node.properties.forEach((property) => {
Expand All @@ -67,9 +97,9 @@ module.exports = function (babel) {
property.key.type === 'Identifier' ? property.key.name : property.key.value;

if (shouldParseScope && propertyName === 'scope') {
result.locals = parseScopeObject(buildError, name, property.value);
result.locals = parseScope(state, buildError, name, property);
} else {
result[propertyName] = parseExpression(buildError, name, property.value);
result[propertyName] = parseExpression(state, buildError, name, property.value);
}
});

Expand Down Expand Up @@ -455,6 +485,7 @@ module.exports = function (babel) {
}

compilerOptions = parseObjectExpression(
state,
path.buildCodeFrameError.bind(path),
options.originalName,
args[1],
Expand Down

0 comments on commit 20fe896

Please sign in to comment.