mirror of
https://github.com/moodle/moodle.git
synced 2025-01-19 06:18:28 +01:00
d851f109ef
There are two phases of a build: Building, and then Outputting. We were previously listening on the final event for the build phase, but we should be listening to the final event of the output phase.
216 lines
7.4 KiB
JavaScript
216 lines
7.4 KiB
JavaScript
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
/* jshint node: true, browser: false */
|
|
/* eslint-env node */
|
|
|
|
/**
|
|
* @copyright 2021 Andrew Nicols
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
/**
|
|
* Function to generate the destination for the minification task
|
|
* (e.g. build/file.min.js). This function will be passed to
|
|
* the rename property of files array when building dynamically:
|
|
* http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically
|
|
*
|
|
* @param {String} destPath the current destination
|
|
* @param {String} srcPath the matched src path
|
|
* @return {String} The rewritten destination path.
|
|
*/
|
|
const babelRename = function(destPath, srcPath) {
|
|
destPath = srcPath.replace(`amd/src`, `amd/build`);
|
|
destPath = destPath.replace(/\.js$/, '.min.js');
|
|
return destPath;
|
|
};
|
|
|
|
module.exports = grunt => {
|
|
// Load the Ignorefiles tasks.
|
|
require('./ignorefiles')(grunt);
|
|
|
|
// Load the Shifter tasks.
|
|
require('./shifter')(grunt);
|
|
|
|
// Load ESLint.
|
|
require('./eslint')(grunt);
|
|
|
|
// Load jsconfig.
|
|
require('./jsconfig')(grunt);
|
|
|
|
// Load JSDoc.
|
|
require('./jsdoc')(grunt);
|
|
|
|
const path = require('path');
|
|
|
|
// Register JS tasks.
|
|
grunt.registerTask('yui', ['eslint:yui', 'shifter']);
|
|
grunt.registerTask('amd', ['ignorefiles', 'eslint:amd', 'rollup']);
|
|
grunt.registerTask('js', ['amd', 'yui']);
|
|
|
|
// Register NPM tasks.
|
|
grunt.loadNpmTasks('grunt-contrib-watch');
|
|
grunt.loadNpmTasks('grunt-rollup');
|
|
|
|
const babelTransform = require('@babel/core').transform;
|
|
const babel = (options = {}) => {
|
|
return {
|
|
name: 'babel',
|
|
|
|
transform: (code, id) => {
|
|
grunt.log.debug(`Transforming ${id}`);
|
|
options.filename = id;
|
|
const transformed = babelTransform(code, options);
|
|
|
|
return {
|
|
code: transformed.code,
|
|
map: transformed.map
|
|
};
|
|
}
|
|
};
|
|
};
|
|
|
|
// Note: We have to use a rate limit plugin here because rollup runs all tasks asynchronously and in parallel.
|
|
// When we kick off a full run, if we kick off a rollup of every file this will fork-bomb the machine.
|
|
// To work around this we use a concurrent Promise queue based on the number of available processors.
|
|
const rateLimit = () => {
|
|
const queue = [];
|
|
let queueRunner;
|
|
|
|
const startQueue = () => {
|
|
if (queueRunner) {
|
|
return;
|
|
}
|
|
|
|
queueRunner = setTimeout(() => {
|
|
const limit = Math.max(1, require('os').cpus().length / 2);
|
|
grunt.log.debug(`Starting rollup with queue size of ${limit}`);
|
|
runQueue(limit);
|
|
}, 100);
|
|
};
|
|
|
|
// The queue runner will run the next `size` items in the queue.
|
|
const runQueue = (size = 1) => {
|
|
queue.splice(0, size).forEach(resolve => {
|
|
grunt.log.debug(`Item resolved. Kicking off next one.`);
|
|
resolve();
|
|
});
|
|
};
|
|
|
|
return {
|
|
name: 'ratelimit',
|
|
|
|
// The options hook is run in parallel.
|
|
// We can return an unresolved Promise which is queued for later resolution.
|
|
options: async(options) => {
|
|
return new Promise(resolve => {
|
|
queue.push(resolve);
|
|
startQueue();
|
|
return options;
|
|
});
|
|
},
|
|
|
|
// When an item in the queue completes, start the next item in the queue.
|
|
generateBundle: (options, bundle) => {
|
|
grunt.log.debug(`Finished output phase for ${Object.keys(bundle).join(', ')}`);
|
|
runQueue();
|
|
},
|
|
};
|
|
};
|
|
|
|
const terser = require('rollup-plugin-terser').terser;
|
|
grunt.config.merge({
|
|
rollup: {
|
|
options: {
|
|
format: 'esm',
|
|
dir: 'output',
|
|
sourcemap: true,
|
|
treeshake: false,
|
|
context: 'window',
|
|
plugins: [
|
|
rateLimit(),
|
|
babel({
|
|
sourceMaps: true,
|
|
comments: false,
|
|
compact: false,
|
|
plugins: [
|
|
'transform-es2015-modules-amd-lazy',
|
|
'system-import-transformer',
|
|
// This plugin modifies the Babel transpiling for "export default"
|
|
// so that if it's used then only the exported value is returned
|
|
// by the generated AMD module.
|
|
//
|
|
// It also adds the Moodle plugin name to the AMD module definition
|
|
// so that it can be imported as expected in other modules.
|
|
path.resolve('.grunt/babel-plugin-add-module-to-define.js')
|
|
],
|
|
presets: [
|
|
['@babel/preset-env', {
|
|
modules: false,
|
|
useBuiltIns: false
|
|
}]
|
|
]
|
|
}),
|
|
|
|
terser({
|
|
// Do not mangle variables.
|
|
// Makes debugging easier.
|
|
mangle: false,
|
|
}),
|
|
],
|
|
},
|
|
dist: {
|
|
files: [{
|
|
expand: true,
|
|
src: grunt.moodleEnv.files ? grunt.moodleEnv.files : grunt.moodleEnv.amdSrc,
|
|
rename: babelRename
|
|
}],
|
|
},
|
|
},
|
|
});
|
|
|
|
grunt.config.merge({
|
|
watch: {
|
|
amd: {
|
|
files: grunt.moodleEnv.inComponent
|
|
? ['amd/src/*.js', 'amd/src/**/*.js']
|
|
: ['**/amd/src/**/*.js'],
|
|
tasks: ['amd']
|
|
},
|
|
},
|
|
});
|
|
|
|
// Add the 'js' task as a startup task.
|
|
grunt.moodleEnv.startupTasks.push('js');
|
|
|
|
// On watch, we dynamically modify config to build only affected files. This
|
|
// method is slightly complicated to deal with multiple changed files at once (copied
|
|
// from the grunt-contrib-watch readme).
|
|
let changedFiles = Object.create(null);
|
|
const onChange = grunt.util._.debounce(function() {
|
|
const files = Object.keys(changedFiles);
|
|
grunt.config('rollup.dist.files', [{expand: true, src: files, rename: babelRename}]);
|
|
changedFiles = Object.create(null);
|
|
}, 200);
|
|
|
|
grunt.event.on('watch', function(action, filepath) {
|
|
changedFiles[filepath] = action;
|
|
onChange();
|
|
});
|
|
|
|
return {
|
|
babelRename,
|
|
};
|
|
};
|