Skip to content

Loader: fontface url() handling lives in the generic src-resolution path #1648

Description

@obiot

loader.load() special-cases one asset type by name in the middle of the shared src-resolution path:

// src/loader/loader.js:534
// strip url() wrapper for fontface assets so baseURL can be prepended to the raw path
if (asset.type === "fontface" && typeof src === "string") {
	const urlMatch = src.match(/^url\(\s*['"]?(.*?)['"]?\s*\)$/);
	if (urlMatch) {
		src = urlMatch[1];
	}
}

That is CSS knowledge sitting in the generic loader, and it is not the only piece: the baseURL guard immediately below also tests src.startsWith("local(") (loader.js:546), which is a CSS font descriptor form too. data: on the next line is genuinely type-agnostic; the other two are not.

Why it cannot simply move to the parser today

The ordering forces it. baseURL is prepended at loader.js:544-549, and the parser only runs afterwards — so by the time preloadFontFace sees data.src, the prefix is already on. Without the strip, a descriptor written as url('x.woff2') would resolve to "data/font/" + "url('x.woff2')", which the parser cannot repair after the fact.

So the result is that one value is unwrapped, prefixed, and then wrapped again in a different file:

loader.js    url('x.woff2')  ->  x.woff2  ->  data/font/x.woff2
fontface.js  data/font/x.woff2  ->  url('data/font/x.woff2')     (parsers/fontface.js:37-40)

with url( handled in both files and local( handled in one.

Suggested shape

parsers is already a Map populated through setParser(type, parserFn) (loader.js:180, :341), so a parser can carry its own resolution rules and the loader can stop naming types:

// parsers/fontface.js
preloadFontFace.resolveSrc = (src) => {
	const m = src.match(/^url\(\s*['"]?(.*?)['"]?\s*\)$/);
	return m ? m[1] : src;
};
preloadFontFace.skipBaseURL = (src) => src.startsWith("local(");

load() then does src = parser.resolveSrc?.(src) ?? src before prefixing, and consults parser.skipBaseURL?.(src) in the guard, keeping only the data: test inline. Both pieces of CSS knowledge end up in the file that understands CSS font descriptors, and the shared path becomes type-agnostic.

Naming is open — a single resolveSrc(src, baseURL) returning the final value would also work, at the cost of each parser repeating the prefix logic. The two-hook split above keeps baseURL resolution in one place.

Notes

  • Behaviour-preserving refactor: no asset should resolve differently afterwards.
  • Existing coverage to keep green: tests/fontface-src.spec.js, tests/loader-asset-src.spec.js, tests/asset-paths-with-spaces.spec.js (a font path containing a space is the case the quoting exists for), and tests/loader.spec.js.
  • Worth checking while in there whether any other parser would benefit from the same hook, or whether fontface stays the only user — if it is the only one, that is an argument for keeping the hook minimal rather than general.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions