html-manipulation.js 3.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. "use strict";
  2. /**
  3. * @license
  4. * Copyright Google LLC All Rights Reserved.
  5. *
  6. * Use of this source code is governed by an MIT-style license that can be
  7. * found in the LICENSE file at https://angular.dev/license
  8. */
  9. Object.defineProperty(exports, "__esModule", { value: true });
  10. exports.appendHtmlElementToHead = appendHtmlElementToHead;
  11. exports.getHtmlHeadTagElement = getHtmlHeadTagElement;
  12. exports.addBodyClass = addBodyClass;
  13. const schematics_1 = require("@angular-devkit/schematics");
  14. const parse5_element_1 = require("./parse5-element");
  15. const parse5_1 = require("parse5");
  16. /** Appends the given element HTML fragment to the `<head>` element of the specified HTML file. */
  17. function appendHtmlElementToHead(host, htmlFilePath, elementHtml) {
  18. const htmlFileBuffer = host.read(htmlFilePath);
  19. if (!htmlFileBuffer) {
  20. throw new schematics_1.SchematicsException(`Could not read file for path: ${htmlFilePath}`);
  21. }
  22. const htmlContent = htmlFileBuffer.toString();
  23. if (htmlContent.includes(elementHtml)) {
  24. return;
  25. }
  26. const headTag = getHtmlHeadTagElement(htmlContent);
  27. if (!headTag) {
  28. throw Error(`Could not find '<head>' element in HTML file: ${htmlFileBuffer}`);
  29. }
  30. // We always have access to the source code location here because the `getHeadTagElement`
  31. // function explicitly has the `sourceCodeLocationInfo` option enabled.
  32. const endTagOffset = headTag.sourceCodeLocation.endTag.startOffset;
  33. const indentationOffset = (0, parse5_element_1.getChildElementIndentation)(headTag);
  34. const insertion = `${' '.repeat(indentationOffset)}${elementHtml}`;
  35. const recordedChange = host.beginUpdate(htmlFilePath).insertRight(endTagOffset, `${insertion}\n`);
  36. host.commitUpdate(recordedChange);
  37. }
  38. /** Parses the given HTML file and returns the head element if available. */
  39. function getHtmlHeadTagElement(htmlContent) {
  40. return getElementByTagName('head', htmlContent);
  41. }
  42. /** Adds a class to the body of the document. */
  43. function addBodyClass(host, htmlFilePath, className) {
  44. const htmlFileBuffer = host.read(htmlFilePath);
  45. if (!htmlFileBuffer) {
  46. throw new schematics_1.SchematicsException(`Could not read file for path: ${htmlFilePath}`);
  47. }
  48. const htmlContent = htmlFileBuffer.toString();
  49. const body = getElementByTagName('body', htmlContent);
  50. if (!body) {
  51. throw Error(`Could not find <body> element in HTML file: ${htmlFileBuffer}`);
  52. }
  53. const classAttribute = body.attrs.find(attribute => attribute.name === 'class');
  54. if (classAttribute) {
  55. const hasClass = classAttribute.value
  56. .split(' ')
  57. .map(part => part.trim())
  58. .includes(className);
  59. if (!hasClass) {
  60. // We have source code location info enabled, and we pre-checked that the element
  61. // has attributes, specifically the `class` attribute.
  62. const classAttributeLocation = body.sourceCodeLocation.attrs['class'];
  63. const recordedChange = host
  64. .beginUpdate(htmlFilePath)
  65. .insertRight(classAttributeLocation.endOffset - 1, ` ${className}`);
  66. host.commitUpdate(recordedChange);
  67. }
  68. }
  69. else {
  70. const recordedChange = host
  71. .beginUpdate(htmlFilePath)
  72. .insertRight(body.sourceCodeLocation.startTag.endOffset - 1, ` class="${className}"`);
  73. host.commitUpdate(recordedChange);
  74. }
  75. }
  76. /** Finds an element by its tag name. */
  77. function getElementByTagName(tagName, htmlContent) {
  78. const document = (0, parse5_1.parse)(htmlContent, { sourceCodeLocationInfo: true });
  79. const nodeQueue = [...document.childNodes];
  80. while (nodeQueue.length) {
  81. const node = nodeQueue.shift();
  82. if (node.nodeName.toLowerCase() === tagName) {
  83. return node;
  84. }
  85. else if (node.childNodes) {
  86. nodeQueue.push(...node.childNodes);
  87. }
  88. }
  89. return null;
  90. }
  91. //# sourceMappingURL=html-manipulation.js.map