@epublishing/grunt-epublishing 0.3.19 → 0.3.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -53,7 +53,7 @@ module.exports = function configureWebpack(grunt, config) {
53
53
  if (!config.babelLoader) {
54
54
  config.babelLoader = {
55
55
  exceptions: [],
56
- exclude: /(node_modules|bower_components)/,
56
+ exclude: /(node_modules)/,
57
57
  };
58
58
  }
59
59
 
@@ -144,7 +144,7 @@ module.exports = function configureWebpack(grunt, config) {
144
144
  rules.push({
145
145
  enforce: "pre",
146
146
  test: /\.js$/,
147
- exclude: /(node_modules|bower_components|public|vendor)/,
147
+ exclude: /(node_modules|public|vendor)/,
148
148
  loader: "eslint-loader",
149
149
  options: {
150
150
  configFile: eslintConfig,
@@ -69,11 +69,6 @@ module.exports = function initJadeConfig(grunt, jadePath, jadeChildPath, jadeChi
69
69
  // Add package.json to the grunt config
70
70
  grunt.config.set('pkg', grunt.file.readJSON('package.json'));
71
71
 
72
- // Check to see if we need to add the bower tasks
73
- if (fs.existsSync('bower.json')) {
74
- jadeTasks.unshift('bower-install-simple', 'bower');
75
- }
76
-
77
72
  // Register the main jade task!
78
73
  grunt.registerTask('jade', jadeTasks);
79
74
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@epublishing/grunt-epublishing",
3
3
  "description": "Automated front-end tasks for ePublishing Jade and client sites.",
4
- "version": "0.3.19",
4
+ "version": "0.3.23",
5
5
  "homepage": "https://www.epublishing.com",
6
6
  "contributors": [
7
7
  {
@@ -54,8 +54,6 @@
54
54
  "grunt": "^1.0.3",
55
55
  "grunt-babel": "^7.0.0",
56
56
  "grunt-bless": "^1.0.2",
57
- "grunt-bower": "^0.21.4",
58
- "grunt-bower-install-simple": "^1.2.6",
59
57
  "grunt-contrib-clean": "^1.1.0",
60
58
  "grunt-contrib-concat": "^1.0.1",
61
59
  "grunt-contrib-uglify": "^3.4.0",
package/tasks/sass.js CHANGED
@@ -2,37 +2,152 @@
2
2
  const util = require('util');
3
3
  const sass = require('sass');
4
4
  const path = require('path');
5
+ const fs = require('fs');
6
+ const fsPromises = require('fs').promises;
7
+ const crypto = require('crypto');
5
8
 
6
9
  module.exports = grunt => {
7
- grunt.registerMultiTask('sass', 'Compile Sass to CSS', function () {
10
+ // Cache for compiled results
11
+ const cache = new Map();
12
+
13
+ // Generate cache key for a file
14
+ const getCacheKey = (filePath, options) => {
15
+ const content = fs.readFileSync(filePath, 'utf8');
16
+ const optionsHash = crypto.createHash('md5').update(JSON.stringify(options)).digest('hex');
17
+ const contentHash = crypto.createHash('md5').update(content).digest('hex');
18
+ return `${filePath}:${contentHash}:${optionsHash}`;
19
+ };
20
+
21
+ // Check if file needs recompilation
22
+ const needsRecompilation = async (src, dest, options) => {
23
+ try {
24
+ const cacheKey = getCacheKey(src, options);
25
+ const cached = cache.get(cacheKey);
26
+
27
+ if (!cached) return true;
28
+
29
+ // Check if destination file exists and is newer than source
30
+ const srcStats = await fsPromises.stat(src);
31
+ const destStats = await fsPromises.stat(dest);
32
+
33
+ return srcStats.mtime > destStats.mtime;
34
+ } catch (error) {
35
+ // If destination doesn't exist or other error, recompile
36
+ return true;
37
+ }
38
+ };
39
+
40
+ grunt.registerMultiTask('sass', 'Compile Sass to CSS with performance optimizations', function () {
8
41
  const done = this.async();
42
+ const startTime = Date.now();
9
43
 
10
44
  const options = this.options({
11
- precision: 10
45
+ precision: 10,
46
+ cache: true,
47
+ parallel: true,
48
+ maxConcurrency: require('os').cpus().length
12
49
  });
13
50
 
14
- (async () => {
15
- await Promise.all(this.files.map(async item => {
51
+ // Filter out partial files and create compilation tasks
52
+ const compilationTasks = this.files
53
+ .map(item => {
16
54
  const [src] = item.src;
17
-
18
55
  if (!src || path.basename(src)[0] === '_') {
56
+ return null;
57
+ }
58
+ return { src, dest: item.dest, options };
59
+ })
60
+ .filter(Boolean);
61
+
62
+ if (compilationTasks.length === 0) {
63
+ grunt.log.writeln('No Sass files to compile');
64
+ done();
65
+ return;
66
+ }
67
+
68
+ // Process files with concurrency control
69
+ const processFile = async (task) => {
70
+ const { src, dest, options: taskOptions } = task;
71
+
72
+ try {
73
+ // Check if recompilation is needed
74
+ if (options.cache && !(await needsRecompilation(src, dest, taskOptions))) {
75
+ grunt.log.debug(`Skipping ${src} (cached)`);
19
76
  return;
20
77
  }
21
78
 
22
- const result = await util.promisify(sass.render)(Object.assign({}, options, {
79
+ // Compile Sass
80
+ const sassOptions = {
81
+ ...taskOptions,
23
82
  file: src,
24
- outFile: item.dest,
25
- }));
83
+ outFile: dest,
84
+ };
85
+
86
+ const result = await util.promisify(sass.render)(sassOptions);
87
+
88
+ // Write CSS file
89
+ await fsPromises.writeFile(dest, result.css);
90
+
91
+ // Write source map if requested
92
+ if (taskOptions.sourceMap) {
93
+ const mapPath = taskOptions.sourceMap === true ? `${dest}.map` : taskOptions.sourceMap;
94
+ await fsPromises.writeFile(mapPath, result.map);
95
+ }
96
+
97
+ // Cache the result
98
+ if (options.cache) {
99
+ const cacheKey = getCacheKey(src, taskOptions);
100
+ cache.set(cacheKey, {
101
+ css: result.css,
102
+ map: result.map,
103
+ timestamp: Date.now()
104
+ });
105
+ }
106
+
107
+ grunt.log.ok(`Compiled ${src} → ${dest}`);
108
+ } catch (error) {
109
+ grunt.log.error(`Error compiling ${src}: ${error.message}`);
110
+ throw error;
111
+ }
112
+ };
26
113
 
27
- grunt.file.write(item.dest, result.css);
114
+ // Process files with controlled concurrency
115
+ const processWithConcurrency = async (tasks, maxConcurrency) => {
116
+ const results = [];
117
+ const chunks = [];
118
+
119
+ // Split tasks into chunks
120
+ for (let i = 0; i < tasks.length; i += maxConcurrency) {
121
+ chunks.push(tasks.slice(i, i + maxConcurrency));
122
+ }
28
123
 
29
- if (options.sourceMap) {
30
- const filePath = options.sourceMap === true ? `${item.dest}.map` : options.sourceMap;
31
- grunt.file.write(filePath, result.map);
124
+ // Process chunks sequentially, but files within each chunk in parallel
125
+ for (const chunk of chunks) {
126
+ const chunkResults = await Promise.all(chunk.map(processFile));
127
+ results.push(...chunkResults);
128
+ }
129
+
130
+ return results;
131
+ };
132
+
133
+ // Main execution
134
+ (async () => {
135
+ try {
136
+ if (options.parallel) {
137
+ await processWithConcurrency(compilationTasks, options.maxConcurrency);
138
+ } else {
139
+ // Sequential processing for debugging or when parallel is disabled
140
+ for (const task of compilationTasks) {
141
+ await processFile(task);
142
+ }
32
143
  }
33
- }));
34
- })().catch(error => {
35
- grunt.fatal(error.formatted || error);
36
- }).then(done);
144
+
145
+ const duration = Date.now() - startTime;
146
+ grunt.log.ok(`Sass compilation completed in ${duration}ms`);
147
+ done();
148
+ } catch (error) {
149
+ grunt.fatal(error.formatted || error.message);
150
+ }
151
+ })();
37
152
  });
38
153
  };