build-js-loader.ts 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. /* eslint-disable no-console */
  2. /* eslint import/no-nodejs-modules:0 */
  3. import fs from 'node:fs';
  4. import {minify} from 'terser';
  5. import * as ts from 'typescript';
  6. /**
  7. * This script is used to generate the loader script templates used by Django.
  8. * It takes the base loader script (which has to be valid ES5 JS!)
  9. * and generates both an unminified and minified version of it, with Django template tags.
  10. *
  11. * The generated Django template tags have to be checked in normally, and are what's actually used.
  12. * The base `.ts` file is only the blueprint used to generate the template files off.
  13. *
  14. * Run this script whenever you change the base loader script,
  15. * then verify the build output of both generated `js.tmpl` files and check all three files in.
  16. */
  17. const header = `{% load sentry_helpers %}`;
  18. const loaderScriptPath = './src/sentry/templates/sentry/js-sdk-loader.ts';
  19. const loaderTmplPath = './src/sentry/templates/sentry/js-sdk-loader.js.tmpl';
  20. const loaderMinTmplPath = './src/sentry/templates/sentry/js-sdk-loader.min.js.tmpl';
  21. async function run() {
  22. const baseTs = fs.readFileSync(loaderScriptPath, 'utf-8');
  23. const {outputText: base} = ts.transpileModule(baseTs, {
  24. compilerOptions: {
  25. noEmitOnError: true,
  26. target: ts.ScriptTarget.ES5,
  27. module: ts.ModuleKind.CommonJS,
  28. },
  29. });
  30. if (!base) {
  31. throw new Error('Could not transpile loader script!');
  32. }
  33. const unminifiedLoader = `${header}${replacePlaceholders(base)}`;
  34. const {code: minifiedBase} = await minify(base, {
  35. ecma: 5,
  36. mangle: {
  37. reserved: ['onLoad', 'forceLoad', 'sentryOnLoad'],
  38. },
  39. format: {
  40. ecma: 5,
  41. },
  42. });
  43. if (!minifiedBase) {
  44. throw new Error('Could not minify loader script!');
  45. }
  46. const minifiedLoader = `${header}${replacePlaceholders(minifiedBase)}\n`;
  47. fs.writeFileSync(loaderTmplPath, unminifiedLoader, 'utf-8');
  48. console.log(`Updated loader script template at ${loaderTmplPath}`);
  49. fs.writeFileSync(loaderMinTmplPath, minifiedLoader, 'utf-8');
  50. console.log(`Updated loader min. script template at ${loaderMinTmplPath}`);
  51. }
  52. /**
  53. * We replace some placeholders in the loader script with Django template tags.
  54. * This is done so the base template is actually valid JS (so we can minify it, have autocomplete, linting, etc.).
  55. * The placeholders are replaced with the actual values by Django.
  56. */
  57. function replacePlaceholders(str: string): string {
  58. return str
  59. .replace('__LOADER__PUBLIC_KEY__', "'{{ publicKey|safe }}'")
  60. .replace('__LOADER_SDK_URL__', "'{{ jsSdkUrl|safe }}'")
  61. .replace('__LOADER__CONFIG__', '{{ config|to_json|safe }}')
  62. .replace('__LOADER__IS_LAZY__', '{{ isLazy|safe|lower }}');
  63. }
  64. run();