1 // This file is part of Moodle - http://moodle.org/
3 // Moodle is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, either version 3 of the License, or
6 // (at your option) any later version.
8 // Moodle is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
15 /* jshint node: true, browser: false */
19 * @copyright 2021 Andrew Nicols
20 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 * Function to generate the destination for the uglify task
25 * (e.g. build/file.min.js). This function will be passed to
26 * the rename property of files array when building dynamically:
27 * http://gruntjs.com/configuring-tasks#building-the-files-object-dynamically
29 * @param {String} destPath the current destination
30 * @param {String} srcPath the matched src path
31 * @return {String} The rewritten destination path.
34 const babelRename = function(destPath, srcPath) {
35 destPath = srcPath.replace('src', 'build');
36 destPath = destPath.replace('.js', '.min.js');
40 module.exports = grunt => {
41 // Load the Ignorefiles tasks.
42 require('./ignorefiles')(grunt);
44 // Load the Shifter tasks.
45 require('./shifter')(grunt);
48 require('./eslint')(grunt);
51 require('./jsconfig')(grunt);
54 require('./jsdoc')(grunt);
56 const path = require('path');
59 grunt.registerTask('yui', ['eslint:yui', 'shifter']);
60 grunt.registerTask('amd', ['ignorefiles', 'eslint:amd', 'rollup']);
61 grunt.registerTask('js', ['amd', 'yui']);
63 // Register NPM tasks.
64 grunt.loadNpmTasks('grunt-contrib-uglify');
65 grunt.loadNpmTasks('grunt-contrib-watch');
66 grunt.loadNpmTasks('grunt-rollup');
68 const babelTransform = require('@babel/core').transform;
69 const babel = (options = {}) => {
73 transform: (code, id) => {
74 grunt.log.debug(`Transforming ${id}`);
75 options.filename = id;
76 const transformed = babelTransform(code, options);
79 code: transformed.code,
86 // Note: We have to use a rate limit plugin here because rollup runs all tasks asynchronously and in parallel.
87 // When we kick off a full run, if we kick off a rollup of every file this will fork-bomb the machine.
88 // To work around this we use a concurrent Promise queue based on the number of available processors.
89 const rateLimit = () => {
93 const startQueue = () => {
98 queueRunner = setTimeout(() => {
99 const limit = Math.max(1, require('os').cpus().length / 2);
100 grunt.log.debug(`Starting rollup with queue size of ${limit}`);
105 // The queue runner will run the next `size` items in the queue.
106 const runQueue = (size = 1) => {
107 queue.splice(0, size).forEach(resolve => {
115 // The options hook is run in parallel.
116 // We can return an unresolved Promise which is queued for later resolution.
117 options: async() => {
118 return new Promise(resolve => {
124 // When an item in the queue completes, start the next item in the queue.
131 const terser = require('rollup-plugin-terser').terser;
147 'transform-es2015-modules-amd-lazy',
148 'system-import-transformer',
149 // This plugin modifies the Babel transpiling for "export default"
150 // so that if it's used then only the exported value is returned
151 // by the generated AMD module.
153 // It also adds the Moodle plugin name to the AMD module definition
154 // so that it can be imported as expected in other modules.
155 path.resolve('.grunt/babel-plugin-add-module-to-define.js'),
156 '@babel/plugin-syntax-dynamic-import',
157 '@babel/plugin-syntax-import-meta',
158 ['@babel/plugin-proposal-class-properties', {'loose': false}],
159 '@babel/plugin-proposal-json-strings'
162 ['@babel/preset-env', {
180 // Do not mangle variables.
181 // Makes debugging easier.
189 src: grunt.moodleEnv.files ? grunt.moodleEnv.files : grunt.moodleEnv.amdSrc,
199 files: grunt.moodleEnv.inComponent
200 ? ['amd/src/*.js', 'amd/src/**/*.js']
201 : ['**/amd/src/**/*.js'],
207 // Add the 'js' task as a startup task.
208 grunt.moodleEnv.startupTasks.push('js');
210 // On watch, we dynamically modify config to build only affected files. This
211 // method is slightly complicated to deal with multiple changed files at once (copied
212 // from the grunt-contrib-watch readme).
213 let changedFiles = Object.create(null);
214 const onChange = grunt.util._.debounce(function() {
215 const files = Object.keys(changedFiles);
216 grunt.config('rollup.dist.files', [{expand: true, src: files, rename: babelRename}]);
217 changedFiles = Object.create(null);
220 grunt.event.on('watch', function(action, filepath) {
221 changedFiles[filepath] = action;