Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion lib/filters/clean_class/definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export const acceptedArguments = [];
*/
export function cleanClass(config, string) {
const identifier = String(string);

if (
!Object.prototype.hasOwnProperty.call(config.cleanClassCache, identifier)
) {
Expand Down
2 changes: 1 addition & 1 deletion lib/filters/clean_class/twing.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { newTwingFilter } from '../../helpers/twing.js';
import config from '../../config.js';
import { name, options, acceptedArguments, cleanClass } from './definition.js';

export async function callable(string) {
export function callable(string) {
return cleanClass(config, string);
}

Expand Down
2 changes: 1 addition & 1 deletion lib/filters/clean_id/twing.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { newTwingFilter } from '../../helpers/twing.js';
import { name, options, acceptedArguments, cleanID } from './definition.js';

export async function callable(...args) {
export function callable(...args) {
return cleanID(...args);
}

Expand Down
15 changes: 12 additions & 3 deletions lib/filters/drupal_escape/twing.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { escape } from 'twing/dist/cjs/lib/extension/core/filters/escape.js';
import { newTwingFilter } from '../../helpers/twing.js';
import { name, options, acceptedArguments } from './definition.js';

export const callable = escape;

// Simple escape function
export function callable(str) {
if (typeof str !== 'string') {
str = String(str);
}
return str
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}
export default newTwingFilter(name, callable, options, acceptedArguments);
2 changes: 1 addition & 1 deletion lib/filters/format_date/twing.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { newTwingFilter } from '../../helpers/twing.js';
import config from '../../config.js';
import { name, options, acceptedArguments, formatDate } from './definition.js';

export async function callable(timestamp, type, format, timezone, langcode) {
export function callable(timestamp, type, format, timezone, langcode) {
return formatDate(config, timestamp, type, format, timezone, langcode);
}

Expand Down
4 changes: 2 additions & 2 deletions lib/filters/placeholder/twing.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import {
wrapPlaceholder,
} from './definition.js';

export async function callable(template, value) {
const escapedValue = await escape(template, value, 'html', null, true);
export function callable(template, value) {
const escapedValue = escape(template, value, 'html', null, true);
return wrapPlaceholder(escapedValue);
}

Expand Down
3 changes: 3 additions & 0 deletions lib/filters/render/definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ export function renderVar(arg) {
// throw errors saying `"0" is an invalid render array key` and print an empty
// string for that variable. Until we implement a renderer, we should return
// an empty string.
if (typeofArg === 'array' && typeof arg[0] === 'string') {
return arg.join('');
}
if (typeofArg === 'array') {
return '';
}
Expand Down
6 changes: 1 addition & 5 deletions lib/filters/render/twing.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import { newTwingFilter } from '../../helpers/twing.js';
import { name, options, acceptedArguments, renderVar } from './definition.js';

export async function callable(...args) {
return renderVar(...args);
}

export default newTwingFilter(name, callable, options, acceptedArguments);
export default newTwingFilter(name, renderVar, options, acceptedArguments);
90 changes: 83 additions & 7 deletions lib/filters/safe_join/twing.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,93 @@
import { join } from 'twing/dist/cjs/lib/extension/core/filters/join.js';
import { isTraversable } from 'twing/dist/cjs/lib/helpers/is-traversable.js';
import { isPlainObject } from 'is-plain-object';
import { newTwingFilter } from '../../helpers/twing.js';
import { name, options, acceptedArguments } from './definition.js';

export async function callable(value, glue) {
// Twing's join() doesn't join Object values like it should. Issue #44
// @see https://gitlab.com/nightlycommit/twing/-/issues/544
// @see https://github.com/JohnAlbin/drupal-twig-extensions/issues/45
function isTraversable(value) {
if (isPlainObject(value)) {
return true;
}
if (value !== null && value !== undefined) {
if (typeof value === 'string') {
return false;
}
if (typeof value['entries'] === 'function') {
return true;
}
if (
typeof value[Symbol.iterator] === 'function' ||
typeof value['next'] === 'function'
) {
return true;
}
}
return false;
}

function iteratorToArray(value) {
if (Array.isArray(value)) {
return value;
} else {
let result = [];
if (value.entries) {
for (let entry of value.entries()) {
result.push(entry[1]);
}
} else if (typeof value[Symbol.iterator] === 'function') {
for (let entry of value) {
result.push(entry);
}
} else if (typeof value['next'] === 'function') {
let next;
while ((next = value.next()) && !next.done) {
result.push(next.value);
}
} else {
for (let k in value) {
result.push(value[k]);
}
}
return result;
}
}

const joinSynchronously = (value, glue, and) => {
if (value == null) {
return '';
}
if (glue == null) {
glue = '';
}
if (isTraversable(value)) {
value = iteratorToArray(value);
// this is ugly, but we have to ensure that each element of the array is rendered as PHP would render it
const safeValue = value.map((item) => {
if (typeof item === 'boolean') {
return item === true ? '1' : '';
}
if (Array.isArray(item)) {
return 'Array';
}
return item;
});
if (and == null || and === glue) {
return safeValue.join(glue);
}
if (safeValue.length === 1) {
return safeValue[0];
}
return (
safeValue.slice(0, -1).join(glue) + and + safeValue[safeValue.length - 1]
);
}
return '';
};

export function callable(value, glue) {
const newValue =
typeof value === 'object' && !isTraversable(value)
? Object.values(value)
: value;
return join(newValue, glue);
return joinSynchronously(newValue, glue);
}

export default newTwingFilter(name, callable, options, acceptedArguments);
13 changes: 2 additions & 11 deletions lib/filters/without/twing.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
import { newTwingFilter } from '../../helpers/twing.js';
import { name, options, without } from './definition.js';

export async function callable(element, argsMap) {
// Twing will give an is_variadic filter its arguments as a Map.
const args = Array.from(argsMap.values()).map((value) => {
if (value instanceof Map) {
// Twing v5 converts Twig [] into JS Maps; convert to an Array.
return Array.from(value.values());
} else {
return value;
}
});
return without(element, ...args);
export function callable(element, ...argsMap) {
return without(element, ...argsMap);
}

export default newTwingFilter(name, callable, options);
2 changes: 1 addition & 1 deletion lib/functions/active_theme/definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const name = 'active_theme';

export const options = {};

export const acceptedArguments = [];
export const acceptedArguments = [{ name: 'name', defaultValue: {} }];

/**
* Gets the name of the active theme.
Expand Down
2 changes: 1 addition & 1 deletion lib/functions/active_theme/twing.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { newTwingFunction } from '../../helpers/twing.js';
import config from '../../config.js';
import { name, options, acceptedArguments, activeTheme } from './definition.js';

export async function callable() {
export function callable() {
return activeTheme(config);
}

Expand Down
2 changes: 1 addition & 1 deletion lib/functions/active_theme_path/definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const name = 'active_theme_path';

export const options = {};

export const acceptedArguments = [];
export const acceptedArguments = [{ name: 'name', defaultValue: {} }];

/**
* Gets the path of the active theme.
Expand Down
2 changes: 1 addition & 1 deletion lib/functions/active_theme_path/twing.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
activeThemePath,
} from './definition.js';

export async function callable() {
export function callable() {
return activeThemePath(config);
}

Expand Down
2 changes: 1 addition & 1 deletion lib/functions/create_attribute/twing.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
createAttribute,
} from './definition.js';

export async function callable(...args) {
export function callable(...args) {
return createAttribute(...args);
}

Expand Down
10 changes: 3 additions & 7 deletions lib/functions/file_url/definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,17 @@ export const acceptedArguments = [{ name: 'uri' }];
export function fileUrl(config, uri) {
// Non-strings are cast to a string with Drupal's file_url.
const path = `${uri}`;

// This regex matches against uri schemes (e.g. 'public://', 'https://'). It
// is copied from \Drupal\Core\StreamWrapper\StreamWrapperManager::getScheme()
const scheme = /^([\w-]+):\/\//;

// If the uri includes a streamWrapper scheme, replace it.
if (
scheme.test(path) &&
Object.keys(config.streamWrapper).includes(path.match(scheme)[0])
) {
return `${config.baseUrl}${path.replace(
scheme,
(substring) => config.streamWrapper[substring] + '/',
)}`;
return `${config.baseUrl}${path.replace(scheme, (substring) => {
return config.streamWrapper[substring] + '/';
})}`;
}

// Allow for:
Expand Down Expand Up @@ -92,7 +89,6 @@ export function fileUrl(config, uri) {
// if ($options['fragment']) {
// $path .= '#' . $options['fragment'];
// }

return path;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/functions/file_url/twing.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { newTwingFunction } from '../../helpers/twing.js';
import config from '../../config.js';
import { name, options, acceptedArguments, fileUrl } from './definition.js';

export async function callable(uri) {
export function callable(uri) {
return fileUrl(config, uri);
}

Expand Down
2 changes: 1 addition & 1 deletion lib/functions/link/twing.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { newTwingFunction } from '../../helpers/twing.js';
import { name, options, acceptedArguments, link } from './definition.js';

export async function callable(...args) {
export function callable(...args) {
return link(...args);
}

Expand Down
8 changes: 2 additions & 6 deletions lib/functions/render_var/twing.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import { newTwingFunction } from '../../helpers/twing.js';
import { name, options, renderVar } from './definition.js';
import { acceptedArguments, name, options, renderVar } from './definition.js';

export async function callable(...args) {
return renderVar(...args);
}

export default newTwingFunction(name, callable, options);
export default newTwingFunction(name, renderVar, options, acceptedArguments);
2 changes: 1 addition & 1 deletion lib/helpers/twing/newEmptyStringFunction.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const newEmptyStringFunction = (
) =>
newTwingFunction(
functionName,
async function () {
function () {
return '';
},
options,
Expand Down
2 changes: 1 addition & 1 deletion lib/helpers/twing/newPassThroughFilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const newPassThroughFilter = (
) =>
newTwingFilter(
filterName,
async function (value) {
function (value) {
return value;
},
options,
Expand Down
2 changes: 1 addition & 1 deletion lib/helpers/twing/newPassThroughFunction.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const newPassThroughFunction = (
) =>
newTwingFunction(
functionName,
async function (value) {
function (value) {
return value;
},
options,
Expand Down
8 changes: 5 additions & 3 deletions lib/helpers/twing/newTwingFilter.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { TwingFilter } from 'twing';
import { createSynchronousFilter } from 'twing';

const newTwingFilter = (name, callable, options, acceptedArguments = []) =>
new TwingFilter(
createSynchronousFilter(
name,
callable,
(_executionContext, ...value) => {
return callable(...value);
},
// @TODO File bug report; 3rd argument should be options.
acceptedArguments,
options,
Expand Down
8 changes: 5 additions & 3 deletions lib/helpers/twing/newTwingFunction.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { TwingFunction } from 'twing';
import { createSynchronousFunction } from 'twing';

const newTwingFunction = (name, callable, options, acceptedArguments = []) =>
new TwingFunction(
createSynchronousFunction(
name,
callable,
(_executionContext, ...value) => {
return callable(...value);
},
// @TODO File bug report; 3rd argument should be options.
acceptedArguments,
options,
Expand Down
2 changes: 1 addition & 1 deletion lib/twing.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import state from './config.js';
/**
* Adds all the extensions to the given Twing environment.
*
* @param {TwingEnvironment} twingEnvironment
* @param { TwingSynchronousEnvironment } twingEnvironment
* The Twing environment to modify.
* @param {Object<string, ?string|Object<string, ?string>>} config
* The Drupal config to use.
Expand Down
Loading