135 lines
4.4 KiB
JavaScript
135 lines
4.4 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", {
|
||
|
value: true
|
||
|
});
|
||
|
exports.default = collectDependencies;
|
||
|
exports.parseFuncIRI = parseFuncIRI;
|
||
|
function _posthtml() {
|
||
|
const data = _interopRequireDefault(require("posthtml"));
|
||
|
_posthtml = function () {
|
||
|
return data;
|
||
|
};
|
||
|
return data;
|
||
|
}
|
||
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||
|
// A list of all attributes that may produce a dependency
|
||
|
// Based on https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute
|
||
|
// See also https://www.w3.org/TR/SVG/attindex.html and https://www.w3.org/TR/SVG11/attindex.html
|
||
|
// SVG animation elements are excluded because they may only reference elements in the same document: https://www.w3.org/TR/SVG/linking.html#processingURL-fetch
|
||
|
const HREF_ATTRS = ['a', 'use', 'feImage', 'image', 'linearGradient', 'radialGradient', 'pattern', 'mpath', 'textPath', 'script'];
|
||
|
const ATTRS = {
|
||
|
href: HREF_ATTRS,
|
||
|
'xlink:href': [...HREF_ATTRS, 'altGlyph', 'cursor', 'filter', 'font-face-uri', 'glyphRef', 'tref', 'color-profile']
|
||
|
};
|
||
|
|
||
|
// Attributes that allow url() to reference another element, either in the same document or a different one.
|
||
|
// https://www.w3.org/TR/SVG11/linking.html#processingIRI
|
||
|
const FUNC_IRI_ATTRS = new Set(['fill', 'stroke', 'clip-path', 'color-profile', 'cursor', 'filter', 'marker', 'marker-start', 'marker-mid', 'marker-end', 'mask',
|
||
|
// SVG2 - https://www.w3.org/TR/SVG/linking.html#processingURL-validity
|
||
|
'shape-inside', 'shape-subtract', 'mask-image']);
|
||
|
|
||
|
// https://www.w3.org/TR/css3-values/#urls
|
||
|
const FUNC_IRI_RE = /^url\((?:((['"])(.*?)\2(\s+.*)?)|((?:\\[\s'"]|[^\s'"])+))\)$/;
|
||
|
const ESCAPE_RE = /\\(.|\n|\r|\u2028|\u2029)/;
|
||
|
function parseFuncIRI(value) {
|
||
|
let m = value.match(FUNC_IRI_RE);
|
||
|
if (m) {
|
||
|
var _m$;
|
||
|
let url = (m[3] || m[5]).replace(ESCAPE_RE, '$1');
|
||
|
let modifier = (_m$ = m[4]) !== null && _m$ !== void 0 ? _m$ : '';
|
||
|
return [url, modifier];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Options to be passed to `addDependency` for certain tags + attributes
|
||
|
const OPTIONS = {
|
||
|
a: {
|
||
|
href: {
|
||
|
needsStableName: true
|
||
|
},
|
||
|
'xlink:href': {
|
||
|
needsStableName: true
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
function collectDependencies(asset, ast) {
|
||
|
let isDirty = false;
|
||
|
const errors = [];
|
||
|
(0, _posthtml().default)().walk.call(ast.program, node => {
|
||
|
// Ideally we'd have location information for specific attributes...
|
||
|
let getLoc = () => node.location ? {
|
||
|
filePath: asset.filePath,
|
||
|
start: node.location.start,
|
||
|
end: node.location.end
|
||
|
} : undefined;
|
||
|
if (typeof node === 'string' && node.startsWith('<?xml-stylesheet')) {
|
||
|
return node.replace(/(?<=(?:^|\s)href\s*=\s*")(.+?)(?=")/i, href => {
|
||
|
isDirty = true;
|
||
|
return asset.addURLDependency(href, {
|
||
|
priority: 'parallel'
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
const {
|
||
|
tag,
|
||
|
attrs
|
||
|
} = node;
|
||
|
if (!attrs) {
|
||
|
return node;
|
||
|
}
|
||
|
for (const attr in attrs) {
|
||
|
// Check for id references
|
||
|
if (attrs[attr][0] === '#') {
|
||
|
continue;
|
||
|
}
|
||
|
const elements = ATTRS[attr];
|
||
|
if (elements && elements.includes(node.tag)) {
|
||
|
var _OPTIONS$tag;
|
||
|
// Check for empty string
|
||
|
if (attrs[attr].length === 0) {
|
||
|
errors.push({
|
||
|
message: `'${attr}' should not be empty string`,
|
||
|
filePath: asset.filePath,
|
||
|
loc: node.location
|
||
|
});
|
||
|
}
|
||
|
let options = (_OPTIONS$tag = OPTIONS[tag]) === null || _OPTIONS$tag === void 0 ? void 0 : _OPTIONS$tag[attr];
|
||
|
if (node.tag === 'script') {
|
||
|
options = {
|
||
|
priority: 'parallel',
|
||
|
env: {
|
||
|
sourceType: attrs.type === 'module' ? 'module' : 'script',
|
||
|
// SVG script elements do not support type="module" natively yet.
|
||
|
outputFormat: 'global',
|
||
|
loc: getLoc()
|
||
|
}
|
||
|
};
|
||
|
delete attrs.type;
|
||
|
}
|
||
|
attrs[attr] = asset.addURLDependency(attrs[attr], {
|
||
|
...options,
|
||
|
loc: getLoc()
|
||
|
});
|
||
|
isDirty = true;
|
||
|
}
|
||
|
if (FUNC_IRI_ATTRS.has(attr)) {
|
||
|
let parsed = parseFuncIRI(attrs[attr]);
|
||
|
if (parsed) {
|
||
|
let depId = asset.addURLDependency(parsed[0], {
|
||
|
loc: getLoc()
|
||
|
});
|
||
|
attrs[attr] = `url('${depId}'${parsed[1]})`;
|
||
|
isDirty = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return node;
|
||
|
});
|
||
|
if (errors.length > 0) {
|
||
|
throw errors;
|
||
|
}
|
||
|
if (isDirty) {
|
||
|
asset.setAST(ast);
|
||
|
}
|
||
|
}
|