builder/lib/tasks/generateCachebusterInfo.js

  1. import crypto from "node:crypto";
  2. import {createResource} from "@ui5/fs/resourceFactory";
  3. import {getLogger} from "@ui5/logger";
  4. const log = getLogger("builder:tasks:generateCachebusterInfo");
  5. /**
  6. * @public
  7. * @module @ui5/builder/tasks/generateCachebusterInfo
  8. */
  9. async function signByTime(resource) {
  10. return resource.getStatInfo().mtime.getTime();
  11. }
  12. async function signByHash(resource) {
  13. const hasher = crypto.createHash("sha1");
  14. const buffer = await resource.getBuffer();
  15. hasher.update(buffer.toString("binary"));
  16. return hasher.digest("hex");
  17. }
  18. function getSigner(type) {
  19. type = type || "time";
  20. switch (type) {
  21. case "time":
  22. return signByTime;
  23. case "hash":
  24. return signByHash;
  25. default:
  26. throw new Error(`Invalid signature type: '${type}'. Valid ones are: 'time' or 'hash'`);
  27. }
  28. }
  29. /* eslint "jsdoc/check-param-names": ["error", {"disableExtraPropertyReporting":true}] */
  30. /**
  31. * Task to generate the application cachebuster info file.
  32. *
  33. * @public
  34. * @function default
  35. * @static
  36. *
  37. * @param {object} parameters Parameters
  38. * @param {@ui5/fs/DuplexCollection} parameters.workspace DuplexCollection to read and write files
  39. * @param {object} parameters.options Options
  40. * @param {string} parameters.options.projectNamespace Namespace of the application
  41. * @param {string} [parameters.options.signatureType='time'] Type of signature to be used ('time' or 'hash')
  42. * @returns {Promise<undefined>} Promise resolving with <code>undefined</code> once data has been written
  43. */
  44. export default function({workspace, options}) {
  45. const {signatureType} = options;
  46. const namespace = options.projectNamespace;
  47. const basePath = `/resources/${namespace}/`;
  48. return workspace.byGlob(`/resources/${namespace}/**/*`)
  49. .then(async (resources) => {
  50. const cachebusterInfo = Object.create(null);
  51. const signer = getSigner(signatureType);
  52. await Promise.all(resources.map(async (resource) => {
  53. let resourcePath = resource.getPath();
  54. if (!resourcePath.startsWith(basePath)) {
  55. log.verbose(
  56. `Ignoring resource with path ${resourcePath} since it is not based on path ${basePath}`);
  57. return;
  58. }
  59. // Remove base path. Absolute paths are not allowed in cachebuster info
  60. resourcePath = resourcePath.replace(basePath, "");
  61. cachebusterInfo[resourcePath] = await signer(resource);
  62. }));
  63. const cachebusterInfoResource = createResource({
  64. path: `/resources/${namespace}/sap-ui-cachebuster-info.json`,
  65. string: JSON.stringify(cachebusterInfo, null, 2)
  66. });
  67. return workspace.write(cachebusterInfoResource);
  68. });
  69. }