script_builders.mjs 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. /**
  2. * @license
  3. * SPDX-License-Identifier: Apache-2.0
  4. */
  5. import '../environment/dev';
  6. import { createScript, unwrapScript } from '../internals/script_impl';
  7. import { assertIsTemplateObject } from '../internals/string_literal';
  8. /**
  9. * Creates a SafeScript object from a template literal (without any embedded
  10. * expressions).
  11. *
  12. * This function is a template literal tag function. It should be called with
  13. * a template literal that does not contain any expressions. For example,
  14. * safeScript`foo`;
  15. *
  16. * @param templateObj This contains the literal part of the template literal.
  17. */
  18. export function safeScript(templateObj) {
  19. if (process.env.NODE_ENV !== 'production') {
  20. assertIsTemplateObject(templateObj, false, 'safeScript is a template literal tag function ' +
  21. 'that only accepts template literals without expressions. ' +
  22. 'For example, safeScript`foo`;');
  23. }
  24. return createScript(templateObj[0]);
  25. }
  26. /** Creates a `SafeScript` value by concatenating multiple `SafeScript`s. */
  27. export function concatScripts(scripts) {
  28. return createScript(scripts.map(unwrapScript).join(''));
  29. }
  30. /**
  31. * Converts a serializable value into JSON that is safe to interpolate into a
  32. * script context. In particular it escapes < characters so that a value of
  33. * "</script>" doesn't break out of the context.
  34. * @param value The value to serialize.
  35. */
  36. export function scriptFromJson(value) {
  37. return createScript(JSON.stringify(value).replace(/</g, '\\x3c'));
  38. }
  39. /**
  40. * Creates a `SafeScript` object from a template literal (without any embedded
  41. * expressions) along with additional arguments that the script should have
  42. * access to. These arguments will be JSON-encoded and passed to the script as
  43. * a function call.
  44. * @example
  45. * ```ts
  46. * safeScriptWithArgs`function (name, props) {
  47. * console.log(name + ' is ' + props.age);
  48. * }`('Bob', { 'age': 42 })
  49. * ```
  50. * would return a `SafeScript` that represents the following code:
  51. * ```js
  52. * (function (name, props) {
  53. * console.log(name + ' is ' + props.age);
  54. * })("Bob",{"age":42})
  55. * ```
  56. * @note Be careful when passing objects as arguments, as unquoted property
  57. * names may be changed during compilation.
  58. * @param templateObj This contains the literal part of the template literal.
  59. * @param emptyArgs Expressions that evaluate to the empty string to enable
  60. * inline comments.
  61. */
  62. export function safeScriptWithArgs(templateObj, ...emptyArgs) {
  63. if (process.env.NODE_ENV !== 'production') {
  64. if (emptyArgs.some(a => a !== '')) {
  65. throw new Error('safeScriptWithArgs only allows empty string expressions ' +
  66. 'to enable inline comments.');
  67. }
  68. assertIsTemplateObject(templateObj, true, 'safeScriptWithArgs is a template literal tag function ' +
  69. 'that only accepts template literals. ' +
  70. 'For example, safeScriptWithArgs`foo`;');
  71. }
  72. return (...argValues) => {
  73. const values = argValues.map((v) => scriptFromJson(v).toString());
  74. return createScript(`(${templateObj.join('')})(${values.join(',')})`);
  75. };
  76. }