Skip to content

Instantly share code, notes, and snippets.

@fuweichin
Last active July 1, 2022 21:02
Show Gist options
  • Select an option

  • Save fuweichin/3b8d7d757f7a08ebeda6e0f0ae5a7dda to your computer and use it in GitHub Desktop.

Select an option

Save fuweichin/3b8d7d757f7a08ebeda6e0f0ae5a7dda to your computer and use it in GitHub Desktop.
given a template literal and a scope object, return resolved string
<!-- to resolve synchronously -->
<script src="resolve-template.js"></script>
<!-- to resolve asynchronously -->
<script>
let worker = new Worker('resolve-template.js');
function resolveTempalteAsync(source, scope) {
return new Promise((resolve, reject) => {
worker.onmessage = (e) => {
let {result, error} = e.data;
if (result) {
resolve(result);
} else if (error) {
reject(error);
} else {
reject(new Error('Invalid message from worker'));
}
};
worker.onerror = (e) => {
reject(new Error('Worker loading error'));
};
worker.postMessage([source, scope]);
});
}
</script>
<!-- usage -->
<script>
/* global resolveTemplate, resolveTempalteAsync */
document.addEventListener('DOMContentLoaded', async () => {
let message1 = resolveTemplate('`Hello ${name}!`', {name: 'World'});
let message2 = await resolveTempalteAsync('`Hello ${name}!`', {name: 'World'});
console.log(message1, message2);
});
</script>
// subset of identifiers, see https://gist.github.com/mathiasbynens/6334847#file-ecmascript-6-js
let es6Identifier = /^[A-Za-z$_][A-Za-z0-9$_]*$/;
// for lsit of reserved words, see https://262.ecma-international.org/12.0/#sec-keywords-and-reserved-words
let reservedWords = new Set(
['await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'import', 'in', 'instanceof', 'new', 'null', 'return', 'super', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield']
.concat(['enum', 'implements', 'interface', 'package', 'private', 'protected', 'public', 'arguments', 'eval'], ['undefined'])
);
/**
* @param {string} source - template literal quoted with grave accent (`)
* @param {object} scope - if a reserved word / non-identifier (e.g. "default", "user-name") is used as property name,
* then use "this", e.g. ${this.default} / ${this['user-name']}, to access the scope object
* @returns {string}
* @example resolveTemplate('`Hello ${name}!`', {name:'World'})
*/
function resolveTemplate(source, scope) {
if (!(source.length >= 2 && source.charAt(0) === '`' && source.charAt(source.length - 1) === '`')) {
throw new TypeError('Invalid template source');
}
let identifiers = Object.getOwnPropertyNames(scope).filter((word) => {
return es6Identifier.test(word) && !reservedWords.has(word);
});
let fn = new Function(`"use strict";${identifiers.length > 0 ? 'let {' + identifiers.join(',') + '}=this;' : ''}return ${source};`);
return fn.call(scope);
}
/* ====== when loaded as worker script ====== */
if (self.importScripts) {
//
self.addEventListener('message', (e) => {
let result = resolveTemplate(...e.data);
self.postMessage({result});
});
self.addEventListener('error', function(e) {
let obj;
let error = e.error;
if (Object.prototype.toString.call(error) === '[object Error]') {
obj = {name: error.name, message: error.message, stack: error.stack};
} else {
obj = {name: 'Error', message: e.message, stack: 'at ' + e.filename + ':' + e.lineno + ':' + e.colno};
}
self.postMessage({error: obj});
});
}
/* ====== to use worker script in main thread ====== */
/*
let worker = new Worker('resolve-template.js');
function resolveTempalteAsync(source, scope) {
return new Promise((resolve, reject) => {
worker.onmessage = (e) => {
let {result, error} = e.data;
if (result) {
resolve(result);
} else if (error) {
reject(error);
} else {
reject(new Error('Invalid message from worker'));
}
};
worker.onerror = (e) => {
reject(new Error('Worker loading error'));
};
worker.postMessage([source, scope]);
});
}
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment