Document application modes (development/debug/production) Add global file drop handler, order column normalization, SPA hash fix Serve CDN assets via /_vendor/ URLs instead of merging into bundles Add production minification with license preservation Improve JSON formatting for debugging and production optimization Add CDN asset caching with CSS URL inlining for production builds Add three-mode system (development, debug, production) Update Manifest CLAUDE.md to reflect helper class architecture Refactor Manifest.php into helper classes for better organization Pre-manifest-refactor checkpoint: Add app_mode documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
114 lines
2.7 KiB
JavaScript
Executable File
114 lines
2.7 KiB
JavaScript
Executable File
/**
|
|
* @typedef SortAttrsParams
|
|
* @property {ReadonlyArray<string>=} order
|
|
* @property {'front' | 'alphabetical'=} xmlnsOrder
|
|
*/
|
|
|
|
export const name = 'sortAttrs';
|
|
export const description = 'Sort element attributes for better compression';
|
|
|
|
/**
|
|
* Sort element attributes for better compression
|
|
*
|
|
* @author Nikolay Frantsev
|
|
*
|
|
* @type {import('../lib/types.js').Plugin<SortAttrsParams>}
|
|
*/
|
|
export const fn = (_root, params) => {
|
|
const {
|
|
order = [
|
|
'id',
|
|
'width',
|
|
'height',
|
|
'x',
|
|
'x1',
|
|
'x2',
|
|
'y',
|
|
'y1',
|
|
'y2',
|
|
'cx',
|
|
'cy',
|
|
'r',
|
|
'fill',
|
|
'stroke',
|
|
'marker',
|
|
'd',
|
|
'points',
|
|
],
|
|
xmlnsOrder = 'front',
|
|
} = params;
|
|
|
|
/**
|
|
* @param {string} name
|
|
* @returns {number}
|
|
*/
|
|
const getNsPriority = (name) => {
|
|
if (xmlnsOrder === 'front') {
|
|
// put xmlns first
|
|
if (name === 'xmlns') {
|
|
return 3;
|
|
}
|
|
// xmlns:* attributes second
|
|
if (name.startsWith('xmlns:')) {
|
|
return 2;
|
|
}
|
|
}
|
|
// other namespaces after and sort them alphabetically
|
|
if (name.includes(':')) {
|
|
return 1;
|
|
}
|
|
// other attributes
|
|
return 0;
|
|
};
|
|
|
|
/**
|
|
* @param {[string, string]} param0
|
|
* @param {[string, string]} param1
|
|
* @returns {number}
|
|
*/
|
|
const compareAttrs = ([aName], [bName]) => {
|
|
// sort namespaces
|
|
const aPriority = getNsPriority(aName);
|
|
const bPriority = getNsPriority(bName);
|
|
const priorityNs = bPriority - aPriority;
|
|
if (priorityNs !== 0) {
|
|
return priorityNs;
|
|
}
|
|
// extract the first part from attributes
|
|
// for example "fill" from "fill" and "fill-opacity"
|
|
const [aPart] = aName.split('-');
|
|
const [bPart] = bName.split('-');
|
|
// rely on alphabetical sort when the first part is the same
|
|
if (aPart !== bPart) {
|
|
const aInOrderFlag = order.includes(aPart) ? 1 : 0;
|
|
const bInOrderFlag = order.includes(bPart) ? 1 : 0;
|
|
// sort by position in order param
|
|
if (aInOrderFlag === 1 && bInOrderFlag === 1) {
|
|
return order.indexOf(aPart) - order.indexOf(bPart);
|
|
}
|
|
// put attributes from order param before others
|
|
const priorityOrder = bInOrderFlag - aInOrderFlag;
|
|
if (priorityOrder !== 0) {
|
|
return priorityOrder;
|
|
}
|
|
}
|
|
// sort alphabetically
|
|
return aName < bName ? -1 : 1;
|
|
};
|
|
|
|
return {
|
|
element: {
|
|
enter: (node) => {
|
|
const attrs = Object.entries(node.attributes);
|
|
attrs.sort(compareAttrs);
|
|
/** @type {Record<string, string>} */
|
|
const sortedAttributes = {};
|
|
for (const [name, value] of attrs) {
|
|
sortedAttributes[name] = value;
|
|
}
|
|
node.attributes = sortedAttributes;
|
|
},
|
|
},
|
|
};
|
|
};
|