diff --git a/__tests__/__snapshots__/index.test.ts.snap b/__tests__/__snapshots__/index.test.ts.snap index 6ca10b0e..e9cdd64e 100644 --- a/__tests__/__snapshots__/index.test.ts.snap +++ b/__tests__/__snapshots__/index.test.ts.snap @@ -186,6 +186,7 @@ exports[`basic resolvers: css 1`] = ` .foo3 { background-image: url(\\"/pubpath/bg.png?query#hash\\"); } +@import url(\\"https://fonts.googleapis.com/css?family=Gudea:400,700\\"); .bar { background-image: url(\\"/pubpath/bg1.png\\"); background-image: url(\\"/pubpath/bg2.testing.regex.png\\"); } @@ -234,6 +235,7 @@ exports[`basic resolvers-hash: css 1`] = ` .foo3 { background-image: url(\\"/pubpath/bg-bd25d3fd.png?query#hash\\"); } +@import url(\\"https://fonts.googleapis.com/css?family=Gudea:400,700\\"); .bar { background-image: url(\\"/pubpath/bg-cc57d19a.png\\"); background-image: url(\\"/pubpath/bg.testing.regex-e13f1639.png\\"); } @@ -282,6 +284,7 @@ exports[`basic resolvers-url-inline: css 1`] = ` .foo3 { background-image: url(\\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGYAAABmBAMAAADL8flRAAAAG1BMVEXMzMyWlpacnJy+vr6jo6PFxcW3t7eqqqqxsbHbm8QuAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAArUlEQVRYhe3QIQ+CUBTF8cPTB8RHsKMTqQQ/gMqmVRkj4+ZmZQRnNBD42F7Afp3N7fxIN/zH2wGIiIiI6J8ZBKk95NMRo23KdaYksz36oKtv47F14et59HdKY69I7AoX9PMMlTPnIrZOe5sHJ18Hc5fDeXKH2n+mpmzgLz6NPWmJNIlNNkA4NvK2ov6ikQ1kqWKYYdggVxNpgtREER6ygTyzbZaRugERERER/eYNUMwTiwsgIHkAAAAASUVORK5CYII=\\"); } +@import url(\\"https://fonts.googleapis.com/css?family=Gudea:400,700\\"); .bar { background-image: url(\\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkBAMAAACCzIhnAAAAG1BMVEXMzMyWlpacnJy+vr6jo6PFxcW3t7eqqqqxsbHbm8QuAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAiklEQVRYhe3QMQ6EIBAF0C+GSInF9mYTs+1ewRsQbmBlayysKefYO2asXbbYxvxHQj6ECQMAEREREf2NQ/fCtp5Zky6vtRMkSJEzhyISynWJnzH6Z8oQlzS7lEc/fLmmQUSvc16OrCPqRl1JePxQYo1ZSWVj9nxrrOb5esw+eXdvzTWfTERERHRXH4tWFZGswQ2yAAAAAElFTkSuQmCC\\"); background-image: url(\\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGUAAABlBAMAAACmUjD8AAAAG1BMVEXMzMyWlpacnJy+vr6jo6PFxcW3t7eqqqqxsbHbm8QuAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAi0lEQVRYhe3QoQ7CMBRG4b/bGipr8DMNFsEL7A2WifkpLEGgl2D62NwRmG2RJOcT7a04SXMlAAAA/LNGh5Mdn1n3m/pS0g7K4Tl857DmSyw1flHyaZFyN9rcjNNcbOQUFZ00XbfZvd+1TTj+0iTf7439rarJYd3/ZjuoamzX1jy683bZrssNAAAApBeYlQ7gQL3QgQAAAABJRU5ErkJggg==\\"); } diff --git a/__tests__/fixtures/resolvers/style.scss b/__tests__/fixtures/resolvers/style.scss index 44fb6cf6..8ba2efc5 100644 --- a/__tests__/fixtures/resolvers/style.scss +++ b/__tests__/fixtures/resolvers/style.scss @@ -1,6 +1,8 @@ -@import "@/foo.css"; +@import url("@/foo.css#hash"); @import "features2/bar"; +@import url("https://fonts.googleapis.com/css?family=Gudea:400,700"); + @font-face { font-family: "Demo"; font-style: normal; diff --git a/src/loaders/postcss/import/index.ts b/src/loaders/postcss/import/index.ts index 24114aee..ac768bc3 100644 --- a/src/loaders/postcss/import/index.ts +++ b/src/loaders/postcss/import/index.ts @@ -1,9 +1,7 @@ import path from "path"; import postcss, { PluginCreator, Result, AtRule } from "postcss"; import valueParser from "postcss-value-parser"; - -import { normalizePath } from "../../../utils/path"; - +import { isAbsolutePath, normalizePath } from "../../../utils/path"; import resolveDefault, { ImportResolve } from "./resolve"; const name = "styles-import"; @@ -92,12 +90,22 @@ const plugin: PluginCreator = (options = {}) => { url = normalizePath(to) + url.slice(from.length); } - // Empty url + // Empty URL if (url.length === 0) { rule.warn(res, `Empty URL in \`${rule.toString()}\``); return; } + // Skip Web URLs + if (!isAbsolutePath(url)) { + try { + new URL(url); + return; + } catch { + // Is not a Web URL, continuing + } + } + importList.push({ rule, url }); }); diff --git a/src/loaders/postcss/import/resolve.ts b/src/loaders/postcss/import/resolve.ts index ef662b81..b39e490c 100644 --- a/src/loaders/postcss/import/resolve.ts +++ b/src/loaders/postcss/import/resolve.ts @@ -1,5 +1,5 @@ import fs from "fs-extra"; - +import { parseUrl } from "query-string"; import resolveAsync from "../../../utils/resolve-async"; /** File resolved by `@import` resolver */ @@ -17,8 +17,10 @@ export type ImportResolve = ( extensions: string[], ) => Promise; -const resolve: ImportResolve = async (url, basedir, extensions) => { +const resolve: ImportResolve = async (inputUrl, basedir, extensions) => { const options = { caller: "@import resolver", basedirs: [basedir], extensions }; + const parseOptions = { parseFragmentIdentifier: true, sort: false as const, decode: false }; + const { url } = parseUrl(inputUrl, parseOptions); const from = await resolveAsync([url, `./${url}`], options); return { from, source: await fs.readFile(from) }; }; diff --git a/src/loaders/postcss/url/index.ts b/src/loaders/postcss/url/index.ts index 7620141d..f6f4f038 100644 --- a/src/loaders/postcss/url/index.ts +++ b/src/loaders/postcss/url/index.ts @@ -182,11 +182,8 @@ const plugin: PluginCreator = (options = {}) => { usedNames.set(to, from); node.type = "string"; - node.value = - publicPath + - (/[/\\]$/.test(publicPath) ? "" : "/") + - path.basename(to) + - (urlQuery ?? ""); + node.value = publicPath + (/[/\\]$/.test(publicPath) ? "" : "/") + path.basename(to); + if (urlQuery) node.value += urlQuery; to = normalizePath(assetDir, to); res.messages.push({ plugin: name, type: "asset", to, source }); diff --git a/src/loaders/postcss/url/resolve.ts b/src/loaders/postcss/url/resolve.ts index 9eea9f61..83ae3356 100644 --- a/src/loaders/postcss/url/resolve.ts +++ b/src/loaders/postcss/url/resolve.ts @@ -1,6 +1,5 @@ import fs from "fs-extra"; -import { ParseOptions, parseUrl, stringifyUrl } from "query-string"; - +import { parseUrl, stringifyUrl } from "query-string"; import resolveAsync from "../../../utils/resolve-async"; /** File resolved by URL resolver */ @@ -18,7 +17,7 @@ export type UrlResolve = (inputUrl: string, basedir: string) => Promise const resolve: UrlResolve = async (inputUrl, basedir) => { const options = { caller: "URL resolver", basedirs: [basedir] }; - const parseOptions: ParseOptions = { parseFragmentIdentifier: true, sort: false, decode: false }; + const parseOptions = { parseFragmentIdentifier: true, sort: false as const, decode: false }; const { url, query, fragmentIdentifier } = parseUrl(inputUrl, parseOptions); const from = await resolveAsync([url, `./${url}`], options); const urlQuery = stringifyUrl({ url: "", query, fragmentIdentifier }, parseOptions);