concat.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /*
  2. * grunt-contrib-concat
  3. * http://gruntjs.com/
  4. *
  5. * Copyright (c) 2013 "Cowboy" Ben Alman, contributors
  6. * Licensed under the MIT license.
  7. */
  8. 'use strict';
  9. module.exports = function(grunt) {
  10. var path = require('path');
  11. function stripBanner(src, options) {
  12. if (!options) {
  13. options = {};
  14. }
  15. var m = [];
  16. if (options.line) {
  17. // Strip // ... leading banners.
  18. m.push('(?:.*\\/\\/.*\\r?\\n)*\\s*');
  19. }
  20. if (options.block) {
  21. // Strips all /* ... */ block comment banners.
  22. m.push('\\/\\*[\\s\\S]*?\\*\\/');
  23. } else {
  24. // Strips only /* ... */ block comment banners, excluding /*! ... */.
  25. m.push('\\/\\*[^!][\\s\\S]*?\\*\\/');
  26. }
  27. var re = new RegExp('^\\s*(?:' + m.join('|') + ')\\s*', '');
  28. return src.replace(re, '');
  29. }
  30. var intro = path.join(__dirname, '../intro.js');
  31. var outro = path.join(__dirname, '../outro.js');
  32. // 排序,把依赖的文件移动到最上面。
  33. function filesFilter( f, files ) {
  34. var cwd = f.cwd || '',
  35. ret = [],
  36. process = function( file ) {
  37. var fileinfo = path.join( cwd, file ),
  38. dirpath = path.dirname( fileinfo ),
  39. depends = [],
  40. str, matches, idx;
  41. if ( !grunt.file.exists( fileinfo ) ) {
  42. return;
  43. }
  44. ret.push( file );
  45. str = grunt.file.read( fileinfo );
  46. // 从require( dps )中找
  47. // 从defind( id?, dps, factory )中找
  48. str = str.replace( /(?:define|require)\s*\(\s*\[([^\]]+?)\],/g, function( _, m1 ) {
  49. m1 = m1.replace(/\s/g, '').split(',');
  50. depends = depends.concat( m1.map(function( item ) {
  51. item = item.substring( 1, item.length - 1 );
  52. item = item.substring(0, 1) === '.' ?
  53. path.join( dirpath, item ):
  54. path.join( cwd, item );
  55. return path.relative( cwd, item ) + '.js';
  56. }) );
  57. return _;
  58. });
  59. str = str.replace( /require\s*\(\s*('|")(.+?)\1/g, function( _, m1, item ) {
  60. item = item.substring(0, 1) === '.' ?
  61. path.join( dirpath, item ):
  62. path.join( cwd, item );
  63. depends.push( path.relative( cwd, item ) + '.js' );
  64. return _;
  65. });
  66. if ( depends.length ) {
  67. depends = depends.filter(function( item, idx, array ) {
  68. return array.indexOf( item ) === idx && grunt.file.exists( path.join( cwd, item ) );
  69. });
  70. idx = ret.indexOf( file );
  71. [].splice.apply( ret, [ idx, 0 ].concat( depends ) );
  72. depends.forEach( process );
  73. }
  74. };
  75. // console.log( files );
  76. files.forEach( process );
  77. ret = ret.filter(function( item, idx, arr ){
  78. return idx === arr.indexOf( item );
  79. });
  80. ret.unshift( path.relative( cwd, intro ) );
  81. ret.push( path.relative( cwd, outro ) );
  82. return ret;
  83. }
  84. // 缓存版本号
  85. var version;
  86. function fileProcess( src, filepath, cwd ) {
  87. var dirpath = path.dirname( filepath );
  88. version = version || grunt.config.get('pkg.version');
  89. src = src.replace( /@version@/g, version );
  90. // 不处理 outro.js
  91. if (filepath.indexOf('outro') >= 0) {
  92. return src;
  93. }
  94. // console.log( filepath, cwd );
  95. // 处理 define( dps ?, factory );
  96. // 处理 require( dps );
  97. src = src.replace( /(define|require)\s*\((?:\s*\[([^\]]+)\],)?/g, function( _, m1, m2 ) {
  98. var str = m1 + '(',
  99. item;
  100. if ( m1 === 'define' ) {
  101. item = path.relative( cwd, filepath );
  102. item = item.substring( 0, item.length - 3 );
  103. str += ' \'' + item.replace(/\\/g, '/') + '\', ';
  104. }
  105. if ( m2 ) {
  106. m2 = m2.replace(/\s/g, '').split(',');
  107. m2 = m2.map(function( item ) {
  108. var _file = item;
  109. _file = _file.substring( 1, _file.length - 1 );
  110. _file = _file.substring(0, 1) === '.' ?
  111. path.join( dirpath, _file ) :
  112. path.join( cwd, _file.substring( 1 ) );
  113. if ( !grunt.file.exists( _file + '.js' ) ) {
  114. return item;
  115. }
  116. _file = path.relative( cwd, _file );
  117. return '\'' + _file.replace(/\\/g, '/').toLowerCase() + '\'';
  118. });
  119. if ( m2.length ) {
  120. str += '[\n ' + m2.join(',\n ') + '\n],';
  121. } else {
  122. str += '[],';
  123. }
  124. }
  125. // console.log( str );
  126. return str;
  127. });
  128. // 处理 require( id );
  129. src = src.replace( /require\s*\(\s*('|")(.+?)\1\s*\)/g, function( _, m1, m2 ) {
  130. var _file = m2;
  131. _file = _file.substring(0, 1) === '.' ?
  132. path.join( dirpath, _file ) :
  133. path.join( cwd, _file.substring( 1 ) );
  134. if ( !grunt.file.exists( _file + '.js' ) ) {
  135. return _;
  136. }
  137. _file = path.relative( cwd, _file );
  138. return 'require(\'' + _file + '\')';
  139. });
  140. return src;
  141. }
  142. grunt.registerMultiTask('concat', 'Concatenate files.', function() {
  143. // Merge task-specific and/or target-specific options with these defaults.
  144. var options = this.options({
  145. separator: grunt.util.linefeed,
  146. banner: '',
  147. footer: '',
  148. stripBanners: false,
  149. process: null,
  150. filesFilter: filesFilter
  151. });
  152. // Normalize boolean options that accept options objects.
  153. if (options.stripBanners === true) {
  154. options.stripBanners = {};
  155. }
  156. if (options.process === true) {
  157. options.process = {};
  158. }
  159. // Process banner and footer.
  160. var banner = grunt.template.process(options.banner);
  161. var footer = grunt.template.process(options.footer);
  162. // Iterate over all src-dest file pairs.
  163. this.files.forEach(function(f) {
  164. var files = f.src;
  165. if (typeof options.filesFilter === 'function') {
  166. files = options.filesFilter( f, files );
  167. }
  168. // Concat banner + specified files + footer.
  169. var cwd = f.cwd || '',
  170. src = banner + files.filter(function(filepath) {
  171. filepath = path.join( cwd, filepath );
  172. // Warn on and remove invalid source files (if nonull was set).
  173. if (!grunt.file.exists(filepath)) {
  174. grunt.log.warn('Source file "' + filepath + '" not found.');
  175. return false;
  176. } else {
  177. return true;
  178. }
  179. }).map(function(filepath) {
  180. filepath = path.join( cwd, filepath );
  181. // Read file source.
  182. var src = grunt.file.read(filepath);
  183. // 文件处理,用来支持amdefine
  184. src = fileProcess( src, filepath, cwd );
  185. // Process files as templates if requested.
  186. if (typeof options.process === 'function') {
  187. src = options.process(src, filepath);
  188. } else if (options.process) {
  189. src = grunt.template.process(src, options.process);
  190. }
  191. // Strip banners if requested.
  192. if (options.stripBanners) {
  193. src = stripBanner(src, options.stripBanners);
  194. }
  195. return src;
  196. }).join(options.separator) + footer;
  197. // Write the destination file.
  198. grunt.file.write(f.dest, src);
  199. // Print a success message.
  200. grunt.log.writeln('File "' + f.dest + '" created.');
  201. });
  202. });
  203. };