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>
154 lines
4.1 KiB
JavaScript
Executable File
154 lines
4.1 KiB
JavaScript
Executable File
/**
|
|
* @typedef RemoveAttrsParams
|
|
* @property {string=} elemSeparator
|
|
* @property {boolean=} preserveCurrentColor
|
|
* @property {string | string[]} attrs
|
|
*/
|
|
|
|
export const name = 'removeAttrs';
|
|
export const description = 'removes specified attributes';
|
|
|
|
const DEFAULT_SEPARATOR = ':';
|
|
const ENOATTRS = `Warning: The plugin "removeAttrs" requires the "attrs" parameter.
|
|
It should have a pattern to remove, otherwise the plugin is a noop.
|
|
Config example:
|
|
|
|
plugins: [
|
|
{
|
|
name: "removeAttrs",
|
|
params: {
|
|
attrs: "(fill|stroke)"
|
|
}
|
|
}
|
|
]
|
|
`;
|
|
|
|
/**
|
|
* Remove attributes
|
|
*
|
|
* @example elemSeparator
|
|
* format: string
|
|
*
|
|
* @example preserveCurrentColor
|
|
* format: boolean
|
|
*
|
|
* @example attrs:
|
|
*
|
|
* format: [ element* : attribute* : value* ]
|
|
*
|
|
* element : regexp (wrapped into ^...$), single * or omitted > all elements (must be present when value is used)
|
|
* attribute : regexp (wrapped into ^...$)
|
|
* value : regexp (wrapped into ^...$), single * or omitted > all values
|
|
*
|
|
* examples:
|
|
*
|
|
* > basic: remove fill attribute
|
|
* ---
|
|
* removeAttrs:
|
|
* attrs: 'fill'
|
|
*
|
|
* > remove fill attribute on path element
|
|
* ---
|
|
* attrs: 'path:fill'
|
|
*
|
|
* > remove fill attribute on path element where value is none
|
|
* ---
|
|
* attrs: 'path:fill:none'
|
|
*
|
|
*
|
|
* > remove all fill and stroke attribute
|
|
* ---
|
|
* attrs:
|
|
* - 'fill'
|
|
* - 'stroke'
|
|
*
|
|
* [is same as]
|
|
*
|
|
* attrs: '(fill|stroke)'
|
|
*
|
|
* [is same as]
|
|
*
|
|
* attrs: '*:(fill|stroke)'
|
|
*
|
|
* [is same as]
|
|
*
|
|
* attrs: '.*:(fill|stroke)'
|
|
*
|
|
* [is same as]
|
|
*
|
|
* attrs: '.*:(fill|stroke):.*'
|
|
*
|
|
*
|
|
* > remove all stroke related attributes
|
|
* ----
|
|
* attrs: 'stroke.*'
|
|
*
|
|
*
|
|
* @author Benny Schudel
|
|
*
|
|
* @type {import('../lib/types.js').Plugin<RemoveAttrsParams>}
|
|
*/
|
|
export const fn = (root, params) => {
|
|
if (typeof params.attrs == 'undefined') {
|
|
console.warn(ENOATTRS);
|
|
return null;
|
|
}
|
|
|
|
const elemSeparator =
|
|
typeof params.elemSeparator == 'string'
|
|
? params.elemSeparator
|
|
: DEFAULT_SEPARATOR;
|
|
const preserveCurrentColor =
|
|
typeof params.preserveCurrentColor == 'boolean'
|
|
? params.preserveCurrentColor
|
|
: false;
|
|
const attrs = Array.isArray(params.attrs) ? params.attrs : [params.attrs];
|
|
|
|
return {
|
|
element: {
|
|
enter: (node) => {
|
|
for (let pattern of attrs) {
|
|
// if no element separators (:), assume it's attribute name, and apply to all elements *regardless of value*
|
|
if (!pattern.includes(elemSeparator)) {
|
|
pattern = ['.*', pattern, '.*'].join(elemSeparator);
|
|
// if only 1 separator, assume it's element and attribute name, and apply regardless of attribute value
|
|
} else if (pattern.split(elemSeparator).length < 3) {
|
|
pattern = [pattern, '.*'].join(elemSeparator);
|
|
}
|
|
|
|
// create regexps for element, attribute name, and attribute value
|
|
const list = pattern.split(elemSeparator).map((value) => {
|
|
// adjust single * to match anything
|
|
if (value === '*') {
|
|
value = '.*';
|
|
}
|
|
return new RegExp(['^', value, '$'].join(''), 'i');
|
|
});
|
|
|
|
// matches element
|
|
if (list[0].test(node.name)) {
|
|
// loop attributes
|
|
for (const [name, value] of Object.entries(node.attributes)) {
|
|
const isCurrentColor = value.toLowerCase() === 'currentcolor';
|
|
const isFillCurrentColor =
|
|
preserveCurrentColor && name == 'fill' && isCurrentColor;
|
|
const isStrokeCurrentColor =
|
|
preserveCurrentColor && name == 'stroke' && isCurrentColor;
|
|
if (
|
|
!isFillCurrentColor &&
|
|
!isStrokeCurrentColor &&
|
|
// matches attribute name
|
|
list[1].test(name) &&
|
|
// matches attribute value
|
|
list[2].test(value)
|
|
) {
|
|
delete node.attributes[name];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
},
|
|
};
|
|
};
|