MTShop/web/modules/contrib/jquery_ui/scripts/generate-libraries.js

205 lines
6.3 KiB
JavaScript

/**
* @file
* This file generates the libraries.yml file automatically.
*
* Each JS file is declared as a library and the dependencies declared in the
* file with the call to 'define' is picked up and automatically added to the
* Drupal library as a dependency.
*
* Manual declarations are kept, only auto-generated declarations are replaced.
*
* There needs to be an empty line between the library declarations for this
* script to work as intended.
*/
const path = require('path');
const { readFile, writeFile } = require('fs').promises;
const { moduleFolder, packageFolder, sourceFolder, filesToCopy } = require('./fileInfos');
(async () => {
// Update the library version in core.libraries.yml with the version
// from the npm package.
const { version } = JSON.parse((await readFile(`${packageFolder}/${sourceFolder}/package.json`)).toString());
// All generated libraries share this metadata. There is no need to use YAML
// variables to simplify, a script is taking care of this.
const libraryMetadata = {
version,
license: {
name: 'Public Domain',
url: `https://raw.githubusercontent.com/jquery/jquery-ui/${version}/LICENSE.txt`,
'gpl-compatible': true,
},
};
/**
* Parse the JS file and extract the necessary dependencies.
*
* This relies on the AMD dependency declaration inside the source files.
* The code looks for a call to `define( [...], factory )` or
* `define( [...] )` and extract the first argument containing the list of
* dependencies.
*
* @param {string} file
*
* @return {Promise<string[]>}
* The list of dependencies as Drupal libraries names.
*/
async function getDependencies(file) {
let out = [];
const absolutePath = `${packageFolder}/${sourceFolder}/ui/${file}.js`;
const folder = path.dirname(absolutePath);
const depsRegex = /define\( ([^)]*) \)/s;
const code = (await readFile(absolutePath)).toString();
if (!depsRegex.test(code)) {
return out;
}
const contents = code.replace(/, factory/g, '');
// Get the array containing the dependencies.
const deps = /define\( ([^)]*) \)/s.exec(contents);
// Use eval instead of JSON.parse because there are comments in the
// dependency list.
out = eval(deps[1]);
return out
// only keep relative dependencies. We hardcode the dependency to jQuery.
.filter((dep) => dep[0] === '.')
.map((dep) => toDrupalDependency(path.resolve(folder, dep)));
}
/**
* Transforms a jQueryUI dependency name into a Drupal library name.
*
* Applies some special rules for effects libraries
*
* @param {string} dependencyPath
*
* @return {string}
*/
function toDrupalDependency(dependencyPath) {
return `${assetModule(dependencyPath)}/${libraryName(dependencyPath)}`;
}
/**
* Get the library name within the module.
*
* @param {string} dependencyPath
*
* @return {string}
*/
function libraryName(dependencyPath) {
let drupalName = dependencyPath.replace(`${packageFolder}/${sourceFolder}/ui/`, '');
if (drupalName.startsWith('widgets/')) {
return drupalName.replace('widgets/', '');
} else if (drupalName === 'effect') {
return 'core';
} else if (drupalName.startsWith('effects/')) {
return drupalName.replace('effects/effect-', '');
} else if (drupalName === 'vendor/jquery-color/jquery.color') {
return 'internal.vendor.jquery.color';
} else if (['core', 'widget', 'position'].includes(drupalName)) {
return drupalName;
}
return `internal.${drupalName}`;
}
function assetModule(dependencyPath) {
let drupalName = dependencyPath.replace(`${packageFolder}/${sourceFolder}/ui/`, '');
if (drupalName === 'widgets/mouse') {
return 'jquery_ui';
}
if (drupalName.startsWith('widgets/')) {
return drupalName.replace('widgets/', 'jquery_ui_');
}
if (drupalName.startsWith('effect')) {
return 'jquery_ui_effects'
}
return 'jquery_ui';
}
/**
* Make sure we set the right weights to the right file.
*
* @param file
*
* @return {object}
*/
function getSettings(file) {
const settings = { minified: true };
if (file === 'effect') {
settings.weight = -9;
} else if (!file.startsWith('effects/')) {
settings.weight = -11;
}
return settings;
}
// Build the list of widgets that needs a CSS file.
const cssFiles = filesToCopy
.filter((file) => path.extname(file) === '.css')
.map((file) => path.basename(file, '.css'));
const jsFiles = filesToCopy
.filter((file) => path.extname(file) === '.js')
.map((file) => file.replace('ui/', '').replace(/\.js$/, ''));
// Create a top-level for all the jquery_ui modules library definition.
const libraries = jsFiles.reduce((acc, file) => {
acc[assetModule(file)] = {};
return acc;
}, {});
// For widgets the CSS dependency is implicit, make it explicit for Drupal
// libraries.
libraries.jquery_ui['internal.widget-css'] = {
...libraryMetadata,
css: {
component: {
'assets/vendor/jquery.ui/themes/base/core.css': {},
},
theme: {
'assets/vendor/jquery.ui/themes/base/theme.css': {},
},
}
}
for (const file of jsFiles) {
const asset = {
...libraryMetadata,
js: {
[`assets/vendor/jquery.ui/ui/${file}-min.js`]: getSettings(file),
},
dependencies: ['core/jquery'].concat(await getDependencies(file)),
};
// The core.js file is deprecated, remove the file but keep the
// dependencies.
if (file === 'core') {
asset.js = {};
}
// All widgets have an implicit dependency on those css files.
if (file.startsWith('widgets/') || ['widget', 'core'].includes(file)) {
asset.dependencies.push('jquery_ui/internal.widget-css');
}
// Check if a CSS file specific to the widget exists.
if (file.startsWith('widgets/')) {
const basename = path.basename(file);
if (cssFiles.includes(basename)) {
asset.css = Object.assign(asset.css || {}, {
component: {[`assets/vendor/jquery.ui/themes/base/${basename}.css`]: {} }
});
}
}
libraries[assetModule(file)][libraryName(file)] = asset;
}
const librariesPath = `${moduleFolder}/jquery_ui.libraries.data.json`;
// Declare the auto-discovered libraries.
await writeFile(librariesPath, JSON.stringify(libraries, null, 2));
})();