Skip to content

Commit

Permalink
feat(import): resolve extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
Anidetrix committed May 8, 2020
1 parent c8f66a6 commit 9005ab8
Show file tree
Hide file tree
Showing 13 changed files with 87 additions and 78 deletions.
2 changes: 1 addition & 1 deletion __tests__/fixtures/resolvers/features/foo.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import "sub/subfoo.css";
@import "sub/subfoo";

.foo {
background-image: url("./bg.png"), url("./bg.testing.regex.png"), url("@/bg.testing.regex.png");
Expand Down
2 changes: 1 addition & 1 deletion __tests__/fixtures/resolvers/style.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@import url(features/foo.css);
@import "features/foo.css";
@import "features2/bar";

.ignore-data-uri {
Expand Down
7 changes: 7 additions & 0 deletions __tests__/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ export async function write(data: WriteData): Promise<WriteResult> {
const bundle = await rollup({
input: fixture(data.input),
plugins: data.plugins ?? [styles(data.options)],
onwarn: (warning, warn) => {
if (warning.code === "EMPTY_BUNDLE") return;
if (warning.source === "lit-element") return;
if (/Exported `\S+` as `\S+` in \S+/.test(warning.message)) return;
if (/Skipping processed file \S+/.test(warning.message)) return;
warn(warning);
},
});

const { output } = await bundle.write({
Expand Down
37 changes: 19 additions & 18 deletions __tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,43 @@ import { mm, getMap, stripMap } from "../src/utils/sourcemap";

describe("load-module", () => {
test("wrong path", async () => {
const wrong = await loadModule("totallyWRONGPATH/here");
expect(wrong).toBeUndefined();
await expect(loadModule("totallyWRONGPATH/here")).resolves.toBeUndefined();
});

test("correct cwd path", async () => {
const correct = await loadModule(fixture("utils/fixture"));
expect(correct).toBe("this is fixture");
await expect(loadModule(fixture("utils/fixture"))).resolves.toBe("this is fixture");
});

test("correct path with custom basepath", async () => {
const correct = await loadModule("fixture", fixture("utils"));
expect(correct).toBe("this is fixture");
await expect(loadModule("fixture", fixture("utils"))).resolves.toBe("this is fixture");
});
});

describe("sourcemap-utils", () => {
test("inline map", async () => {
let code =
'.foo {color: red;background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkBAMAAACCzIhnAAAAG1BMVEXMzMyWlpacnJy+vr6jo6PFxcW3t7eqqqqxsbHbm8QuAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAiklEQVRYhe3QMQ6EIBAF0C+GSInF9mYTs+1ewRsQbmBlayysKefYO2asXbbYxvxHQj6ECQMAEREREf2NQ/fCtp5Zky6vtRMkSJEzhyISynWJnzH6Z8oQlzS7lEc/fLmmQUSvc16OrCPqRl1JePxQYo1ZSWVj9nxrrOb5esw+eXdvzTWfTERERHRXH4tWFZGswQ2yAAAAAElFTkSuQmCC");}';
const noMap = await getMap(code);
expect(noMap).toBeUndefined();

await expect(getMap(code)).resolves.toBeUndefined();

code +=
"/*# sourceMappingURL=data:application/json;base64,e1RISVM6SVNBU09VUkNFTUFQU0lNVUxBVElPTn0= */";
const correctMap = await getMap(code);
expect(correctMap).toBe("{THIS:ISASOURCEMAPSIMULATION}");

await expect(getMap(code)).resolves.toBe("{THIS:ISASOURCEMAPSIMULATION}");
});

test("file map", async () => {
const code = ".foo {color: red;}/*# sourceMappingURL=fixture.css.map */";
const noPathMap = await getMap(code);
expect(noPathMap).toBeUndefined();
const wrongPathMap = await getMap(code, "this/is/nonexistant/path.css");
expect(wrongPathMap).toBeUndefined();
const correctMap = await getMap(code, fixture("utils/pointless.css"));
expect(correctMap).toBe("{THIS:ISASOURCEMAPSIMULATION}");

await expect(getMap(code)).rejects.toMatchObject(
new Error("Extracted map detected, but no ID is provided"),
);

await expect(getMap(code, "this/is/nonexistant/path.css")).resolves.toBeUndefined();

await expect(getMap(code, fixture("utils/pointless.css"))).resolves.toBe(
"{THIS:ISASOURCEMAPSIMULATION}",
);
});

test("strip map", () => {
Expand All @@ -50,7 +52,6 @@ describe("sourcemap-utils", () => {
const map = JSON.stringify({ sources: ["../a/b/../foo/bar.css", "../b/a/../bar/foo.css"] });
const relativeSrc = JSON.stringify(mm(map).relative().toObject()?.sources);
expect(relativeSrc).toBe(JSON.stringify(["../a/foo/bar.css", "../b/bar/foo.css"]));
const wrongMap = mm("thisisnotjson").toString();
expect(wrongMap).toBeUndefined();
expect(mm("thisisnotjson").toString()).toBeUndefined();
});
});
46 changes: 20 additions & 26 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 6 additions & 10 deletions src/loaders/postcss/icss/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,15 @@ export type InteroperableCSSOptions = {

const plugin: postcss.Plugin<InteroperableCSSOptions> = postcss.plugin(
name,
options => async (css, res): Promise<void> => {
(options = {}) => async (css, res): Promise<void> => {
if (!css.source?.input.file) return;
if (!res.processor) return;

const load = options?.load ?? loadDefault;
const extensions = options?.extensions ?? [".css"];
const load = options.load ?? loadDefault;
const extensions = options.extensions ?? [".css", ".pcss", ".postcss", ".sss"];

const opts = res.opts
? {
...res.opts,
map: typeof res.opts.map === "object" ? { ...res.opts.map, prev: false } : res.opts.map,
}
: {};
const opts = res.opts && { ...res.opts };
delete opts?.map;

const { icssExports, icssImports } = extractICSS(css);

Expand All @@ -48,7 +44,7 @@ const plugin: postcss.Plugin<InteroperableCSSOptions> = postcss.plugin(

res.messages.push({ plugin: name, type: "icss", replacements });

if (typeof options?.getReplacements === "function")
if (typeof options.getReplacements === "function")
options.getReplacements(css.source.input.file, replacements, res.opts?.to);
},
);
Expand Down
4 changes: 2 additions & 2 deletions src/loaders/postcss/icss/resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ export default async function (
processor: postcss.Processor,
opts?: postcss.ProcessOptions,
): Promise<Replacements> {
return Object.entries(icssImports).reduce(async (result, [url, values]) => {
return Object.entries(icssImports).reduce(async (acc, [url, values]) => {
const exports = await load(url, file, extensions, processor, opts);
const mappedValues = Object.entries(values).reduce<Replacements>((acc, [k, v]) => {
acc[k] = exports[v];
return acc;
}, {});
return { ...(await result), ...mappedValues };
return { ...(await acc), ...mappedValues };
}, Promise.resolve({}));
}
26 changes: 14 additions & 12 deletions src/loaders/postcss/import/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import resolveDefault, { Resolve } from "./resolve";

const name = "styles-import";

/** `@import` handler options */
export type ImportOptions = {
/**
* Provide custom resolver for imports
Expand All @@ -22,20 +23,18 @@ export type ImportOptions = {
alias?: { [from: string]: string };
};

const plugin: postcss.Plugin<ImportOptions> = postcss.plugin(
type ImportPrivateOptions = { extensions?: string[] };
const plugin: postcss.Plugin<ImportOptions & ImportPrivateOptions> = postcss.plugin(
name,
options => async (css, res): Promise<void> => {
(options = {}) => async (css, res): Promise<void> => {
if (!css.source?.input.file) return;

const resolve = options?.resolve ?? resolveDefault;
const alias = options?.alias ?? {};
const resolve = options.resolve ?? resolveDefault;
const alias = options.alias ?? {};
const extensions = options.extensions ?? [".css", ".pcss", ".postcss", ".sss"];

const opts = res.opts
? {
...res.opts,
map: typeof res.opts.map === "object" ? { ...res.opts.map, prev: false } : res.opts.map,
}
: {};
const opts = res.opts && { ...res.opts };
delete opts?.map;

const { file } = css.source.input;
const importMap = new Map<postcss.AtRule, string>();
Expand Down Expand Up @@ -96,9 +95,12 @@ const plugin: postcss.Plugin<ImportOptions> = postcss.plugin(

for await (const [importRule, url] of importMap) {
try {
const { source, from } = await resolve(url, basedir);
const { source, from } = await resolve(url, basedir, extensions);

if (!(source instanceof Uint8Array) || typeof from !== "string") continue;
if (!(source instanceof Uint8Array) || typeof from !== "string") {
importRule.warn(res, `Incorrectly resolved \`@import\` in \`${importRule.toString()}\``);
continue;
}

if (normalizePath(from) === normalizePath(file)) {
importRule.warn(res, `\`@import\` loop in \`${importRule.toString()}\``);
Expand Down
9 changes: 6 additions & 3 deletions src/loaders/postcss/import/resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import fs from "fs-extra";

import resolveAsync from "../../../utils/resolve-async";

/** File resolved by `@import` resolver */
export type ResolvedFile = { from: string; source: Uint8Array };
export type Resolve = (url: string, basedir: string) => Promise<ResolvedFile>;

const resolve: Resolve = async (url, basedir) => {
const options = { basedir };
/** `@import` resolver */
export type Resolve = (url: string, basedir: string, extensions: string[]) => Promise<ResolvedFile>;

const resolve: Resolve = async (url, basedir, extensions) => {
const options = { basedir, extensions };
let from: string;
try {
from = await resolveAsync(url, options);
Expand Down
2 changes: 1 addition & 1 deletion src/loaders/postcss/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const loader: Loader<PostCSSLoaderOptions> = {

const plugins = [
...[
options.import && postcssImport(options.import),
options.import && postcssImport({ ...options.import, extensions: options.extensions }),
options.url && postcssUrl(options.url),
].filter(booleanFilter),
...(options.postcss.plugins ?? []),
Expand Down

0 comments on commit 9005ab8

Please sign in to comment.