build-content-styles.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. /**
  2. * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. /* eslint-env node */
  6. const path = require( 'path' );
  7. const fs = require( 'fs' );
  8. const webpack = require( 'webpack' );
  9. const { styles } = require( '@ckeditor/ckeditor5-dev-utils' );
  10. const DESTINATION_DIRECTORY = path.join( __dirname, '..', '..', 'build', 'content-styles' );
  11. const contentRules = [];
  12. const webpackConfig = getWebpackConfig();
  13. runWebpack( webpackConfig )
  14. .then( () => {
  15. const data = contentRules
  16. .map( rule => {
  17. // Removes all comments from the rule definition.
  18. const cssAsArray = rule.css.replace( /\/\*[^*]+\*\//g, '' ).split( '\n' );
  19. // We want to fix invalid indentations. We need to find a number of how many indentations we want to remove.
  20. // Because the last line ends the block, we can use this value.
  21. const lastLineIndent = cssAsArray[ cssAsArray.length - 1 ].length - 1;
  22. const css = cssAsArray
  23. .filter( line => line.trim().length > 0 )
  24. .map( ( line, index ) => {
  25. // Do not touch the first line. It is always correct.
  26. if ( index === 0 ) {
  27. return line;
  28. }
  29. return line.slice( lastLineIndent );
  30. } )
  31. .join( '\n' );
  32. return `/* ${ rule.file } */\n${ css }`;
  33. } )
  34. .filter( rule => {
  35. // 1st: path to the css file, 2nd: selector definition - start block, 3rd: end block
  36. // If the rule contains only 3 lines, it means that it does not define any rules.
  37. return rule.split( '\n' ).length > 3;
  38. } )
  39. .join( '\n' );
  40. return writeFile( path.join( DESTINATION_DIRECTORY, 'content-styles.css' ), data );
  41. } )
  42. .then( () => {
  43. console.log( `Content styles has saved under the path: ${ path.join( DESTINATION_DIRECTORY, 'content-styles.css' ) }` );
  44. } )
  45. .catch( err => {
  46. console.log( err );
  47. } );
  48. /**
  49. * Prepares configuration for Webpack.
  50. *
  51. * @returns {Object}
  52. */
  53. function getWebpackConfig() {
  54. const postCssConfig = styles.getPostCssConfig( {
  55. themeImporter: {
  56. themePath: require.resolve( '@ckeditor/ckeditor5-theme-lark' )
  57. },
  58. minify: false
  59. } );
  60. const contentStylesPlugin = require( './content-styles/list-content-styles' )( { contentRules } );
  61. postCssConfig.plugins.push( contentStylesPlugin );
  62. return {
  63. mode: 'development',
  64. devtool: 'source-map',
  65. entry: {
  66. ckeditor5: path.join( __dirname, 'content-styles', 'ckeditor.js' )
  67. },
  68. output: {
  69. path: DESTINATION_DIRECTORY,
  70. filename: '[name].js'
  71. },
  72. // Configure the paths so building CKEditor 5 snippets work even if the script
  73. // is triggered from a directory outside ckeditor5 (e.g. multi-project case).
  74. resolve: {
  75. modules: getModuleResolvePaths()
  76. },
  77. resolveLoader: {
  78. modules: getModuleResolvePaths()
  79. },
  80. module: {
  81. rules: [
  82. {
  83. test: /\.svg$/,
  84. use: [ 'raw-loader' ]
  85. },
  86. {
  87. test: /\.css$/,
  88. use: [
  89. 'style-loader',
  90. {
  91. loader: 'postcss-loader',
  92. options: postCssConfig
  93. }
  94. ]
  95. }
  96. ]
  97. }
  98. };
  99. }
  100. /**
  101. * @param {Object} webpackConfig
  102. * @returns {Promise}
  103. */
  104. function runWebpack( webpackConfig ) {
  105. return new Promise( ( resolve, reject ) => {
  106. webpack( webpackConfig, ( err, stats ) => {
  107. if ( err ) {
  108. reject( err );
  109. } else if ( stats.hasErrors() ) {
  110. reject( new Error( stats.toString() ) );
  111. } else {
  112. resolve();
  113. }
  114. } );
  115. } );
  116. }
  117. /**
  118. * @returns {Array.<String>}
  119. */
  120. function getModuleResolvePaths() {
  121. return [
  122. path.resolve( __dirname, '..', '..', 'node_modules' ),
  123. 'node_modules'
  124. ];
  125. }
  126. function writeFile( file, data ) {
  127. return new Promise( ( resolve, reject ) => {
  128. fs.writeFile( file, data, err => {
  129. if ( err ) {
  130. return reject( err );
  131. }
  132. return resolve();
  133. } );
  134. } );
  135. }