rework.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. /*
  2. * MIT License http://opensource.org/licenses/MIT
  3. * Author: Ben Holloway @bholloway
  4. */
  5. 'use strict';
  6. var path = require('path'),
  7. convert = require('convert-source-map'),
  8. rework = require('rework'),
  9. visit = require('rework-visit');
  10. var fileProtocol = require('../file-protocol');
  11. /**
  12. * Process the given CSS content into reworked CSS content.
  13. *
  14. * @param {string} sourceFile The absolute path of the file being processed
  15. * @param {string} sourceContent CSS content without source-map
  16. * @param {{outputSourceMap: boolean, transformDeclaration:function, absSourceMap:object,
  17. * sourceMapConsumer:object}} params Named parameters
  18. * @return {{content: string, map: object}} Reworked CSS and optional source-map
  19. */
  20. function process(sourceFile, sourceContent, params) {
  21. // embed source-map in css
  22. // prepend file protocol to all sources to avoid problems with source map
  23. var contentWithMap = sourceContent + (
  24. params.absSourceMap ?
  25. convert.fromObject(fileProtocol.prepend(params.absSourceMap)).toComment({multiline: true}) :
  26. ''
  27. );
  28. // need to prepend file protocol to source as well to avoid problems with source map
  29. var reworked = rework(contentWithMap, {source: fileProtocol.prepend(sourceFile)})
  30. .use(reworkPlugin)
  31. .toString({
  32. sourcemap : params.outputSourceMap,
  33. sourcemapAsObject: params.outputSourceMap
  34. });
  35. // complete with source-map
  36. if (params.outputSourceMap) {
  37. return {
  38. content: reworked.code,
  39. map : fileProtocol.remove(reworked.map)
  40. };
  41. }
  42. // complete without source-map
  43. else {
  44. return {
  45. content: reworked,
  46. map : null
  47. };
  48. }
  49. /**
  50. * Plugin for css rework that follows SASS transpilation.
  51. *
  52. * @param {object} stylesheet AST for the CSS output from SASS
  53. */
  54. function reworkPlugin(stylesheet) {
  55. // visit each node (selector) in the stylesheet recursively using the official utility method
  56. // each node may have multiple declarations
  57. visit(stylesheet, function visitor(declarations) {
  58. if (declarations) {
  59. declarations.forEach(eachDeclaration);
  60. }
  61. });
  62. /**
  63. * Process a declaration from the syntax tree.
  64. * @param declaration
  65. */
  66. function eachDeclaration(declaration) {
  67. var isValid = declaration.value && (declaration.value.indexOf('url') >= 0);
  68. if (isValid) {
  69. // reverse the original source-map to find the original source file before transpilation
  70. var startPosApparent = declaration.position.start,
  71. startPosOriginal = params.sourceMapConsumer &&
  72. params.sourceMapConsumer.originalPositionFor(startPosApparent);
  73. // we require a valid directory for the specified file
  74. var directory =
  75. startPosOriginal &&
  76. startPosOriginal.source &&
  77. fileProtocol.remove(path.dirname(startPosOriginal.source));
  78. if (directory) {
  79. declaration.value = params.transformDeclaration(declaration.value, directory);
  80. }
  81. // source-map present but invalid entry
  82. else if (params.sourceMapConsumer) {
  83. throw new Error('source-map information is not available at url() declaration');
  84. }
  85. }
  86. }
  87. }
  88. }
  89. module.exports = process;