diff --git a/.gitignore b/.gitignore
index cc04ae26f86..08f2ef7b164 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,3 +55,4 @@ moodle-plugin-ci.phar
/admin/tool/componentlibrary/hugo/site/data/my-index.json
.hugo_build.lock
.phpcs.xml
+jsconfig.json
diff --git a/.grunt/components.js b/.grunt/components.js
index 848632db6dd..9f1651e94ab 100644
--- a/.grunt/components.js
+++ b/.grunt/components.js
@@ -233,6 +233,7 @@ const getOwningComponentDirectory = checkPath => {
};
module.exports = {
+ fetchComponentData,
getAmdSrcGlobList,
getComponentFromPath,
getComponentPaths,
diff --git a/.grunt/tasks/javascript.js b/.grunt/tasks/javascript.js
index 9533f18db64..b22abaa73b1 100644
--- a/.grunt/tasks/javascript.js
+++ b/.grunt/tasks/javascript.js
@@ -47,6 +47,9 @@ module.exports = grunt => {
// Load ESLint.
require('./eslint')(grunt);
+ // Load jsconfig.
+ require('./jsconfig')(grunt);
+
// Load JSDoc.
require('./jsdoc')(grunt);
diff --git a/.grunt/tasks/jsconfig.js b/.grunt/tasks/jsconfig.js
new file mode 100644
index 00000000000..9e32c2ebcbe
--- /dev/null
+++ b/.grunt/tasks/jsconfig.js
@@ -0,0 +1,53 @@
+// 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 .
+/* jshint node: true, browser: false */
+/* eslint-env node */
+
+/**
+ * @copyright 2022 Andrew Lyons
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+const configuration = {
+ compilerOptions: {
+ baseUrl: ".",
+ paths: {
+ },
+ target: "es2015",
+ allowSyntheticDefaultImports: false,
+ },
+ exclude: [
+ "node_modules",
+ ],
+ include: [],
+};
+
+module.exports = (grunt) => {
+ const handler = () => {
+ const jsconfigData = Object.assign({}, configuration);
+
+ const path = require('path');
+ const {fetchComponentData} = require(path.join(process.cwd(), '.grunt', 'components.js'));
+
+ const componentData = fetchComponentData().components;
+ for (const [thisPath, component] of Object.entries(componentData)) {
+ jsconfigData.compilerOptions.paths[`${component}/*`] = [`${thisPath}/amd/src/*`];
+ jsconfigData.include.push(`${thisPath}/amd/src/**/*`);
+ }
+
+ grunt.file.write('jsconfig.json', JSON.stringify(jsconfigData, null, " ") + "\n");
+ };
+ grunt.registerTask('jsconfig', 'Generate a jsconfig configuration compatible with the LSP', handler);
+};