cdn-resources-service.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import loggerFactory from '~/utils/logger';
  2. import { resolveFromRoot } from '~/utils/project-dir-utils';
  3. const { URL } = require('url');
  4. const urljoin = require('url-join');
  5. const { envUtils } = require('@growi/core');
  6. const cdnLocalScriptRoot = 'public/static/js/cdn';
  7. const cdnLocalScriptWebRoot = '/static/js/cdn';
  8. const cdnLocalStyleRoot = 'public/static/styles/cdn';
  9. const cdnLocalStyleWebRoot = '/static/styles/cdn';
  10. class CdnResourcesService {
  11. constructor() {
  12. this.logger = loggerFactory('growi:service:CdnResourcesService');
  13. this.loadManifests();
  14. }
  15. get noCdn() {
  16. return envUtils.toBoolean(process.env.NO_CDN);
  17. }
  18. loadManifests() {
  19. this.cdnManifests = require('^/resource/cdn-manifests');
  20. this.logger.debug('manifest data loaded : ', this.cdnManifests);
  21. }
  22. getScriptManifestByName(name) {
  23. const manifests = this.cdnManifests.js
  24. .filter((manifest) => { return manifest.name === name });
  25. return (manifests.length > 0) ? manifests[0] : null;
  26. }
  27. getStyleManifestByName(name) {
  28. const manifests = this.cdnManifests.style
  29. .filter((manifest) => { return manifest.name === name });
  30. return (manifests.length > 0) ? manifests[0] : null;
  31. }
  32. /**
  33. * download all resources from CDN and write to FS
  34. *
  35. * !! This method should be invoked only by /bin/download-cdn-resources when build client !!
  36. *
  37. * @param {CdnResourceDownloader} cdnResourceDownloader
  38. */
  39. async downloadAndWriteAll(cdnResourceDownloader) {
  40. const CdnResource = require('~/models/cdn-resource');
  41. const cdnScriptResources = this.cdnManifests.js.map((manifest) => {
  42. const outDir = resolveFromRoot(cdnLocalScriptRoot);
  43. return new CdnResource(manifest.name, manifest.url, outDir);
  44. });
  45. const cdnStyleResources = this.cdnManifests.style.map((manifest) => {
  46. const outDir = resolveFromRoot(cdnLocalStyleRoot);
  47. return new CdnResource(manifest.name, manifest.url, outDir);
  48. });
  49. const dlStylesOptions = {
  50. replaceUrl: {
  51. webroot: cdnLocalStyleWebRoot,
  52. },
  53. };
  54. return Promise.all([
  55. cdnResourceDownloader.downloadScripts(cdnScriptResources),
  56. cdnResourceDownloader.downloadStyles(cdnStyleResources, dlStylesOptions),
  57. ]);
  58. }
  59. /**
  60. * Generate script tag string
  61. *
  62. * @param {Object} manifest
  63. */
  64. generateScriptTag(manifest) {
  65. const attrs = [];
  66. const args = manifest.args || {};
  67. if (args.async) {
  68. attrs.push('async');
  69. }
  70. if (args.defer) {
  71. attrs.push('defer');
  72. }
  73. // TODO process integrity
  74. const url = this.noCdn
  75. ? `${urljoin(cdnLocalScriptWebRoot, manifest.name)}.js`
  76. : manifest.url;
  77. return `<script src="${url}" ${attrs.join(' ')}></script>`;
  78. }
  79. getScriptTagByName(name) {
  80. const manifest = this.getScriptManifestByName(name);
  81. return this.generateScriptTag(manifest);
  82. }
  83. getScriptTagsByGroup(group) {
  84. return this.cdnManifests.js
  85. .filter((manifest) => {
  86. return manifest.groups != null && manifest.groups.includes(group);
  87. })
  88. .map((manifest) => {
  89. return this.generateScriptTag(manifest);
  90. });
  91. }
  92. /**
  93. * Generate style tag string
  94. *
  95. * @param {Object} manifest
  96. */
  97. generateStyleTag(manifest) {
  98. const attrs = [];
  99. const args = manifest.args || {};
  100. if (args.async) {
  101. attrs.push('async');
  102. }
  103. if (args.defer) {
  104. attrs.push('defer');
  105. }
  106. // TODO process integrity
  107. const url = this.noCdn
  108. ? `${urljoin(cdnLocalStyleWebRoot, manifest.name)}.css`
  109. : manifest.url;
  110. return `<link rel="stylesheet" href="${url}" ${attrs.join(' ')}>`;
  111. }
  112. getStyleTagByName(name) {
  113. const manifest = this.getStyleManifestByName(name);
  114. return this.generateStyleTag(manifest);
  115. }
  116. getStyleTagsByGroup(group) {
  117. return this.cdnManifests.style
  118. .filter((manifest) => {
  119. return manifest.groups != null && manifest.groups.includes(group);
  120. })
  121. .map((manifest) => {
  122. return this.generateStyleTag(manifest);
  123. });
  124. }
  125. getHighlightJsStyleTag(styleName) {
  126. let manifest = this.getStyleManifestByName('highlight-theme-github');
  127. // replace style
  128. if (!this.noCdn) {
  129. const url = new URL(`${styleName}.css`, manifest.url); // resolve `${styleName}.css` from manifest.url
  130. // clone manifest
  131. manifest = Object.assign(manifest, { url: url.toString() });
  132. }
  133. return this.generateStyleTag(manifest);
  134. }
  135. }
  136. module.exports = CdnResourcesService;