New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@ordergroove/smi-precompile

Package Overview
Dependencies
Maintainers
4
Versions
80
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ordergroove/smi-precompile - npm Package Compare versions

Comparing version 1.7.11-alpha-PR-766-9.48 to 1.7.11-alpha-PR-819-4.45

1

index.js
export * as loader from './loader';
export * as precompile from './precompile';

41

lit-nunjucks.js

@@ -344,33 +344,20 @@ const nunjucks = require('nunjucks');

const array = this.wrap(node.arr);
const renderArrayItem = t.arrowFunctionExpression(
[
node.name instanceof n.Array
? t.objectPattern(
node.name.children.map(it =>
t.objectProperty(t.identifier(it.value), t.identifier(it.value), false, true)
const repeatCallExpression = t.callExpression(t.memberExpression(array, t.identifier('map')), [
t.arrowFunctionExpression(
[
node.name instanceof n.Array
? t.objectPattern(
node.name.children.map(it =>
t.objectProperty(t.identifier(it.value), t.identifier(it.value), false, true)
)
)
)
: this.wrap(node.name),
t.identifier('index')
],
this.wrapTemplate(node.body)
);
const mapCallExpression = t.callExpression(t.memberExpression(array, t.identifier('map')), [renderArrayItem]);
const repeatCallExpression = t.callExpression(t.identifier('repeat'), [
array,
t.arrowFunctionExpression(
[t.identifier('item')],
// using the | key filter sets this property on each item
// we use it as a unique key to allow Lit to update lists correctly when the order changes
t.memberExpression(t.identifier('item'), t.identifier('__og_key'))
),
renderArrayItem
: this.wrap(node.name),
t.identifier('index')
],
this.wrapTemplate(node.body)
)
]);
const isArray = t.callExpression(t.identifier('Array.isArray'), [array]);
// when the | key filter is used in the template, the resulting array will have this property set
const shouldKey = t.memberExpression(array, t.identifier('__og_should_key'));
const conditionalMapOrRepeat = t.conditionalExpression(shouldKey, repeatCallExpression, mapCallExpression);
return t.conditionalExpression(

@@ -381,3 +368,3 @@ node.else_

conditionalMapOrRepeat,
repeatCallExpression,
node.else_ ? this.wrapTemplate(node.else_) : t.stringLiteral('')

@@ -384,0 +371,0 @@ );

@@ -56,9 +56,5 @@ /* eslint-disable */

describe('forloop', () => {
function expectedForLoop(renderCode, renderElse = '""') {
return `{ return Array.isArray(list) ? list.__og_should_key ? repeat(list, item => item.__og_key, ${renderCode}) : list.map(${renderCode}) : ${renderElse}; }`;
}
test('should repeat items', () => {
expect(compile(`{%for item in list %}{{item}}{% endfor%}`)).toEqual(
toCode(expectedForLoop('(item, index) => item'), 'list')
toCode('{ return Array.isArray(list) ? list.map((item, index) => item) : "" }', 'list')
);

@@ -68,3 +64,3 @@ });

expect(compile(`{%for name, value in list %}{{name}}{% endfor%}`)).toEqual(
toCode(expectedForLoop('({ name, value }, index) => name'), 'list')
toCode('{ return Array.isArray(list) ? list.map(({name, value}, index) => name) : ""; }', 'list')
);

@@ -74,3 +70,3 @@ });

expect(compile(`{%for item in list %}<div>{{item}}</div>{% endfor%}`)).toEqual(
toCode(expectedForLoop('(item, index) => html`<div>${item}</div>`'), 'list')
toCode('{ return Array.isArray(list) ? list.map((item, index) => html`<div>${item}</div>`) : ""; }', 'list')
);

@@ -80,6 +76,3 @@ });

expect(compile(`{%for item in list %}{{item}}{%else%}nothing{% endfor%}`)).toEqual(
toCode(
'{ return Array.isArray(list) && list.length ? list.__og_should_key ? repeat(list, item => item.__og_key, (item, index) => item) : list.map((item, index) => item) : html`nothing`; }',
'list'
)
toCode('{return Array.isArray(list) && list.length ? list.map((item, index) => item) : html`nothing`; }', 'list')
);

@@ -89,7 +82,7 @@ });

expect(compile(`{% for i in "foobar" | list %}{{ i }},{% endfor %}`)).toEqual(
toCode(
'{ return Array.isArray(_F.list("foobar")) ? _F.list("foobar").__og_should_key ? repeat(_F.list("foobar"), item => item.__og_key, (i, index) => html`${i},`) : _F.list("foobar").map((i, index) => html`${i},`) : ""; }'
)
toCode('{return Array.isArray(_F.list("foobar")) ? _F.list("foobar").map((i, index) => html`${i},`):"";}')
);
});
it('should loop thru objects', () => {});
});

@@ -96,0 +89,0 @@ describe('group', () => {

{
"name": "@ordergroove/smi-precompile",
"version": "1.7.11-alpha-PR-766-9.48+0c765774",
"version": "1.7.11-alpha-PR-819-4.45+00ece7c6",
"description": "Prcompilers for smi-core",

@@ -50,3 +50,3 @@ "author": "Brian Lewis <brian.lewis@ordergroove.com>",

},
"gitHead": "0c7657749f2aab940967db17b32e030c88105b93"
"gitHead": "00ece7c65a893961279fb5aaa7445ca65533cd62"
}

@@ -0,1 +1,2 @@

const { nodes: n } = require('nunjucks');
const babel = require('@babel/parser');

@@ -5,3 +6,3 @@ const { default: generate } = require('@babel/generator');

const t = require('@babel/types');
const { parse } = require('./lit-nunjucks');
const { Parser } = require('./lit-nunjucks');
const { get, snakeCase, last, mapKeys, has } = require('lodash');

@@ -13,2 +14,41 @@ const path = require('path');

const validCurrencies = new Set(Object.values(i18nCurrency));
const validCurrencyDisplays = new Set(['symbol', 'narrowSymbol', 'code', 'name']);
class SmiParser extends Parser {
handleFunCall(node) {
if (node.name instanceof n.Symbol) {
switch (node.name.value) {
case 't':
return this.handleTranslation(node);
case 'setting':
return this.handleSetting(node);
case 'js':
return this.handleJs(node);
case 'currency':
return this.handleCurrency(node);
default:
return super.handleFunCall(node);
}
}
}
handleTranslation(node) {
return t.callExpression(t.identifier(`_F.t`), [].concat(this.wrap(node.args)));
}
handleSetting(node) {
return t.callExpression(t.identifier(`_F.setting`), [].concat(this.wrap(node.args)));
}
handleJs(node) {
return t.callExpression(t.identifier(`_F.js`), [].concat(this.wrap(node.args)));
}
handleCurrency(node) {
return t.callExpression(t.identifier(`_F.currency`), [].concat(this.wrap(node.args)));
}
}
function parse(source, opts) {
return new SmiParser(opts).parse(source);
}
const filenameWithoutExt = (content, file) => {

@@ -66,132 +106,136 @@ const ext = path.extname(file);

/**
* @param source string representing the main template liquid file
* @param partials string object where keys refer to the template partials name and values are the template content
* @param locales array locale objects { locale, translations, file }
* @param defaultLocale string default to en
*/
function precompileCompat(source, partials = {}, locales = [], settings = {}, defaultLocale = 'en', script, css) {
const ast = t.file(t.program([parse(source, { partials: mapKeys(partials, filenameWithoutExt) })]));
function addCallExpressions(ast, locales, file, settings) {
traverse(ast, {
// replace | t expressions
CallExpression(path) {
const { arguments: args } = path.node;
const [firstArg] = args;
const result = [];
const localizedTeamplates = [];
const validCurrencies = new Set(Object.values(i18nCurrency));
const validCurrencyDisplays = new Set(['symbol', 'narrowSymbol', 'code', 'name']);
if (isTranslationCallExpression(path) && t.isStringLiteral(firstArg)) {
processTranslationCallExpression(path, firstArg, locales, file);
} else if (isJsCallExpression(path) && t.isStringLiteral(firstArg)) {
processJsCallExpression(path, firstArg);
} else if (isSettingCallExpression(path) && t.isStringLiteral(firstArg)) {
processSettingCallExpression(path, firstArg, settings);
} else if (isCurrencyCallExpression(path)) {
processCurrencyCallExpression(path, locales, validCurrencies, validCurrencyDisplays);
}
},
Identifier(path) {
const translationsKey = path.node.name;
if (translationsKey in locales) {
processIdentifier(path, translationsKey, locales, file);
}
}
});
}
if (!locales.length) {
locales.push({
locale: defaultLocale,
translations: {}
});
function processTranslationCallExpression(path, firstArg, locales, file) {
const translationsKey = firstArg.value.toLowerCase();
const translationArguments = path.node.arguments[1];
if (!(translationsKey in locales) || locales[translationsKey] === '') {
path.replaceWith(t.stringLiteral(''));
} else if (!translationArguments) {
path.replaceWith(parseTranslation(locales[translationsKey], translationsKey, file));
} else if (t.isObjectExpression(translationArguments)) {
replaceTranslationWithArguments(path, translationArguments, locales[translationsKey], translationsKey, file);
} else {
throw new Error('Translation expression not supported');
}
}
let defaultLocaleIx = locales.findIndex(({ locale }) => locale.startsWith(defaultLocale));
if (defaultLocaleIx < 0) defaultLocaleIx = 0;
function processJsCallExpression(path, firstArg) {
try {
const subprogram = babel.parse(firstArg.value);
path.replaceWith(subprogram.program.body[0]);
} catch (err) {
throw new Error(`Can't parse JS expression: ${firstArg.value}`);
}
}
locales.forEach(({ locale, translations, file }, ix, arr) => {
const localeName = locale;
const locales =
ix !== defaultLocaleIx ? { ...arr[defaultLocaleIx].translations, ...translations } : translations || {};
const clonedAst = t.cloneNode(ast, true);
traverse(clonedAst, {
// replace | t expressions
CallExpression(path) {
if (isTranslationCallExpression(path) && t.isStringLiteral(path.node.arguments[0])) {
const translationsKey = path.node.arguments[0].value.toLowerCase();
function processSettingCallExpression(path, firstArg, settings) {
const settingKey = firstArg.value.toLowerCase();
const defaultValueNode = path.node.arguments[1];
if (!(translationsKey in locales) || locales[translationsKey] === '') {
path.replaceWith(t.stringLiteral(''));
} else if (path.node.arguments.length === 1) {
path.replaceWith(parseTranslation(locales[translationsKey], translationsKey, file));
} else if (path.node.arguments.length === 2 && t.isObjectExpression(path.node.arguments[1])) {
// Support translations arguments t(every=subscription.every, period=subscription.period)
const argumentReplacement = path.node.arguments[1];
if (!argumentReplacement.properties.every(v => t.isStringLiteral(v.key))) {
throw new Error('Translation arguments keys should be string');
}
if (has(settings, settingKey)) {
const value = get(settings, settingKey);
path.replaceWithSourceString(JSON.stringify(value));
} else if (defaultValueNode) {
path.replaceWith(defaultValueNode);
} else {
path.replaceWithSourceString('undefined');
}
}
const [argumentNames, callValues] = argumentReplacement.properties.reduce(
(acc, cur) => [
[...acc[0], t.identifier(cur.key.value)],
[...acc[1], cur.value]
],
[[], []]
);
const body = parseTranslation(locales[translationsKey], translationsKey, file);
const translationWithValues = t.callExpression(t.arrowFunctionExpression(argumentNames, body), callValues);
path.replaceWith(translationWithValues);
} else {
throw new Error('translation expression not supported');
}
}
if (isJsCallExpression(path) && t.isStringLiteral(path.node.arguments[0])) {
try {
const subprogram = babel.parse(path.node.arguments[0].value);
path.replaceWith(subprogram.program.body[0]);
} catch (err) {
throw new Error(`Cant parse expression, ${path.node.arguments[0].value}`);
}
}
if (isSettingCallExpression(path) && t.isStringLiteral(path.node.arguments[0])) {
const settingKey = path.node.arguments[0].value.toLowerCase();
if (has(settings, settingKey)) {
const value = get(settings, settingKey);
path.replaceWithSourceString(JSON.stringify(value));
} else if (path.node.arguments[1]) {
path.replaceWith(path.node.arguments[1]);
} else {
path.replaceWithSourceString('undefined');
}
}
function processCurrencyCallExpression(path, locales, validCurrencies, validCurrencyDisplays) {
// there should always be a value being piped into the filter
if (path.node.arguments.length === 1) {
path.node.arguments.push(
validCurrencies.has(locales['currency_code'])
? t.stringLiteral(locales['currency_code'])
: t.identifier('undefined')
);
path.node.arguments.push(
validCurrencyDisplays.has(locales['currency_display'])
? t.stringLiteral(locales['currency_display'])
: t.identifier('undefined')
);
}
}
// there should always be a value being piped into the filter
if (isCurrencyCallExpression(path) && path.node.arguments.length === 1) {
path.node.arguments.push(
validCurrencies.has(locales['currency_code'])
? t.stringLiteral(locales['currency_code'])
: t.identifier('undefined')
);
path.node.arguments.push(
validCurrencyDisplays.has(locales['currency_display'])
? t.stringLiteral(locales['currency_display'])
: t.identifier('undefined')
);
}
},
Identifier(path) {
const translationsKey = path.node.name;
if (translationsKey in locales) {
if (propertyIsInTemplateArg(path)) {
path.parentPath.remove();
} else if (t.isTemplateLiteral(path.parent) || isTranslation(path)) {
if (locales[translationsKey] === '') {
path.replaceWith(t.stringLiteral(''));
} else {
path.replaceWith(parseTranslation(locales[translationsKey], translationsKey, file));
}
}
}
}
});
function processIdentifier(path, translationsKey, locales, file) {
if (propertyIsInTemplateArg(path)) {
path.parentPath.remove();
} else if (t.isTemplateLiteral(path.parent) || isTranslation(path)) {
if (locales[translationsKey] === '') {
path.replaceWith(t.stringLiteral(''));
} else {
path.replaceWith(parseTranslation(locales[translationsKey], translationsKey, file));
}
}
}
traverse(clonedAst, {
Identifier(path) {
if (t.isFunctionDeclaration(path.parent) && path.node.name === 'template') {
const name = `template_${snakeCase(localeName)}`;
localizedTeamplates.push(t.objectProperty(t.stringLiteral(localeName), t.identifier(name)));
result.push(
t.functionDeclaration(
t.identifier(name),
path.parent.params.map(p => t.cloneNode(p, true)),
t.cloneNode(path.parent.body, true)
)
);
}
function replaceTranslationWithArguments(path, argumentReplacement, translation, translationsKey, file) {
// Support translations arguments t(every=subscription.every, period=subscription.period)
if (!argumentReplacement.properties.every(prop => t.isStringLiteral(prop.key))) {
throw new Error('Translation argument keys should be strings');
}
const [argumentNames, callValues] = argumentReplacement.properties.reduce(
([names, values], prop) => [
[...names, t.identifier(prop.key.value)],
[...values, prop.value]
],
[[], []]
);
const body = parseTranslation(translation, translationsKey, file);
const translationWithValues = t.callExpression(t.arrowFunctionExpression(argumentNames, body), callValues);
path.replaceWith(translationWithValues);
}
function addLocaleFunctions(ast, localeName, result, localizedTemplates) {
traverse(ast, {
Identifier(path) {
if (t.isFunctionDeclaration(path.parent) && path.node.name === 'template') {
const name = `template_${snakeCase(localeName)}`;
localizedTemplates.push(t.objectProperty(t.stringLiteral(localeName), t.identifier(name)));
result.push(
t.functionDeclaration(
t.identifier(name),
path.parent.params.map(p => t.cloneNode(p, true)),
t.cloneNode(path.parent.body, true)
)
);
}
});
}
});
}
function handleExtras(settings, script, css) {
let extras = [];
if (script) {
const scriptAst = babel.parse(script);
extras = [...extras, ...scriptAst.program.body];
extras.push(...scriptAst.program.body);
}

@@ -207,4 +251,33 @@ if (css) {

`);
extras = [...extras, ...cssAst.program.body];
extras.push(...cssAst.program.body);
}
return extras;
}
/**
* @param source string representing the main template liquid file
* @param partials string object where keys refer to the template partials name and values are the template content
* @param locales array locale objects { locale, translations, file }
* @param defaultLocale string default to en
*/
function precompileCompat(source, partials = {}, locales = [], settings = {}, defaultLocale = 'en', script, css) {
const ast = t.file(t.program([parse(source, { partials: mapKeys(partials, filenameWithoutExt) })]));
const result = [];
const localizedTemplates = [];
if (!locales.length) locales.push({ locale: defaultLocale, translations: {} });
let defaultLocaleIx = locales.findIndex(({ locale }) => locale.startsWith(defaultLocale));
if (defaultLocaleIx < 0) defaultLocaleIx = 0;
locales.forEach(({ locale, translations, file }, ix, arr) => {
const localeName = locale;
const mergedLocales =
ix !== defaultLocaleIx ? { ...arr[defaultLocaleIx].translations, ...translations } : translations || {};
const clonedAst = t.cloneNode(ast, true);
addCallExpressions(clonedAst, mergedLocales, file, settings);
addLocaleFunctions(clonedAst, localeName, result, localizedTemplates);
});
const extras = handleExtras(settings, script, css);
const program = t.file(

@@ -215,8 +288,4 @@ t.program([

[t.identifier('html'), t.identifier('repeat'), t.identifier('unsafeHTML')],
t.blockStatement([
// Custom script should be before templates_en_us but inside template(){} generator.
...extras,
...result,
t.returnStatement(t.objectExpression(localizedTeamplates))
])
// Custom script should be before templates_en_us but inside template(){} generator.
t.blockStatement([...extras, ...result, t.returnStatement(t.objectExpression(localizedTemplates))])
)

@@ -303,2 +372,3 @@ ])

}
/**

@@ -305,0 +375,0 @@ * new signature

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc