Skip to content

Commit

Permalink
perf: misc optimizations
Browse files Browse the repository at this point in the history
  • Loading branch information
Anidetrix committed Jun 17, 2020
1 parent 5811ce7 commit df1d1ee
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 76 deletions.
4 changes: 2 additions & 2 deletions __tests__/fixtures/modules-duplication/postcss.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = {
module.exports = () => ({
plugins: {
"postcss-custom-properties": {},
},
};
});
14 changes: 7 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,10 @@ export default (options: Options = {}): Plugin => {
const hashable = extracted
.filter(e => ids.includes(e.id))
.sort((a, b) => ids.lastIndexOf(a.id) - ids.lastIndexOf(b.id))
.map(e => ({
...e,
id: path.basename(e.id),
map: mm(e.map).relative(path.dirname(e.id)).toString(),
}));
.map(e => {
const { base, dir } = path.parse(e.id);
return { ...e, id: base, map: mm(e.map).relative(dir).toString() };
});

if (hashable.length === 0) return;

Expand All @@ -137,7 +136,8 @@ export default (options: Options = {}): Plugin => {
async generateBundle(opts, bundle) {
if (extracted.length === 0 || !(opts.dir || opts.file)) return;

const dir = opts.dir ?? path.dirname(opts.file ?? "");
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const dir = opts.dir ?? path.dirname(opts.file!);
const chunks = Object.values(bundle).filter((c): c is OutputChunk => c.type === "chunk");
const emitted = preserveModules ? chunks : chunks.filter(c => c.isEntry || c.isDynamicEntry);
const emittedList: [string, string[]][] = [];
Expand Down Expand Up @@ -180,7 +180,7 @@ export default (options: Options = {}): Plugin => {
};

const getName = (chunk: OutputChunk): string => {
if (opts.file) return path.basename(opts.file, path.extname(opts.file));
if (opts.file) return path.parse(opts.file).name;
if (preserveModules) {
const { dir, name } = path.parse(chunk.fileName);
return dir ? `${dir}/${name}` : name;
Expand Down
25 changes: 11 additions & 14 deletions src/loaders/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,13 @@ interface LoadersOptions {
}

export default class Loaders {
readonly #use: Map<string, Record<string, unknown>>;
readonly #test: (file: string) => boolean;
readonly #loaders = new Map<string, Loader>();
private readonly use: Map<string, Record<string, unknown>>;
private readonly test: (file: string) => boolean;
private readonly loaders = new Map<string, Loader>();

constructor(options: LoadersOptions) {
this.#use = new Map(options.use.reverse());

this.#test = (file): boolean =>
options.extensions.some(ext => file.toLowerCase().endsWith(ext));

this.use = new Map(options.use.reverse());
this.test = (file): boolean => options.extensions.some(ext => file.toLowerCase().endsWith(ext));
this.addLoader(postcssLoader);
this.addLoader(sourcemapLoader);
this.addLoader(sassLoader);
Expand All @@ -50,21 +47,21 @@ export default class Loaders {
}

addLoader<T extends Record<string, unknown>>(loader: Loader<T>): void {
if (!this.#use.has(loader.name)) return;
this.#loaders.set(loader.name, loader as Loader);
if (!this.use.has(loader.name)) return;
this.loaders.set(loader.name, loader as Loader);
}

isSupported(file: string): boolean {
if (this.#test(file)) return true;
for (const [, loader] of this.#loaders) {
if (this.test(file)) return true;
for (const [, loader] of this.loaders) {
if (matchFile(file, loader.test)) return true;
}
return false;
}

async process(payload: Payload, context: LoaderContext): Promise<Payload> {
for await (const [name, options] of this.#use) {
const loader = this.#loaders.get(name);
for await (const [name, options] of this.use) {
const loader = this.loaders.get(name);
if (!loader) continue;
const ctx: LoaderContext = { ...context, options };
if (loader.alwaysProcess || matchFile(ctx.id, loader.test)) {
Expand Down
37 changes: 8 additions & 29 deletions src/loaders/postcss/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,30 @@ import path from "path";
import { RawSourceMap } from "source-map";
import { makeLegalIdentifier } from "@rollup/pluginutils";
import postcss from "postcss";
import postcssrc from "postcss-load-config";
import cssnano from "cssnano";
import { PostCSSLoaderOptions } from "../../types";
import { humanlizePath, normalizePath } from "../../utils/path";
import { mm } from "../../utils/sourcemap";
import resolveAsync from "../../utils/resolve-async";
import safeId from "../../utils/safe-id";
import { Loader } from "../types";
import loadConfig from "./config";
import postcssImport from "./import";
import postcssUrl from "./url";
import postcssModules from "./modules";
import postcssICSS from "./icss";
import postcssNoop from "./noop";

let injectorId: string;
const testing = process.env.NODE_ENV === "test";
const reservedWords = ["css"];

function getClassNameDefault(name: string): string {
const id = makeLegalIdentifier(name);
if (reservedWords.includes(id)) return `_${id}`;
return id;
}

type LoadedConfig = ReturnType<typeof postcssrc> extends PromiseLike<infer T> ? T : never;
async function loadConfig(
id: string,
config: PostCSSLoaderOptions["config"],
): Promise<Partial<LoadedConfig>> {
const configPath =
typeof config === "object" && config.path ? path.resolve(config.path) : path.dirname(id);

const context: Record<string, Record<string, unknown>> = {
file: {
extname: path.extname(id),
dirname: path.dirname(id),
basename: path.basename(id),
},
options: typeof config === "object" ? config.ctx ?? {} : {},
};

return postcssrc(context, configPath).catch(error => {
if (!/no postcss config found/i.test((error as Error).message)) throw error;
return {};
});
}

function ensureAutoModules(am: PostCSSLoaderOptions["autoModules"], id: string): boolean {
if (typeof am === "function") return am(id);
if (am instanceof RegExp) return am.test(id);
Expand Down Expand Up @@ -95,7 +74,7 @@ const loader: Loader<PostCSSLoaderOptions> = {
const modulesOptions = typeof options.modules === "object" ? options.modules : {};
plugins.push(
...postcssModules({
generateScopedName: process.env.NODE_ENV === "test" ? "[name]_[local]" : undefined,
generateScopedName: testing ? "[name]_[local]" : undefined,
failOnWrongOrder: true,
...modulesOptions,
}),
Expand All @@ -116,12 +95,15 @@ const loader: Loader<PostCSSLoaderOptions> = {
case "warning":
this.warn({ name: msg.plugin, message: msg.text as string });
break;

case "icss":
Object.assign(modulesExports, msg.export as Record<string, string>);
break;

case "dependency":
this.deps.add(normalizePath(msg.file));
break;

case "asset":
this.assets.set(msg.to, msg.source);
break;
Expand Down Expand Up @@ -176,10 +158,7 @@ const loader: Loader<PostCSSLoaderOptions> = {

if (!injectorId) {
injectorId = await resolveAsync("./inject-css", {
basedir: path.join(
process.env.NODE_ENV === "test" ? process.cwd() : __dirname,
"runtime",
),
basedir: path.join(testing ? process.cwd() : __dirname, "runtime"),
}).then(normalizePath);
}

Expand Down
11 changes: 6 additions & 5 deletions src/loaders/postcss/modules/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@ import hasher from "../../../utils/hasher";
import { hashRe } from "../common";

export default (placeholder = "[name]_[local]__[hash:8]") => (
name: string,
local: string,
file: string,
css: string,
): string => {
const hash = hasher(`${path.basename(file)}:${css}`);
const { dir, name, base } = path.parse(file);
const hash = hasher(`${base}:${css}`);
const match = hashRe.exec(placeholder);
const hashLen = match && Number.parseInt(match[1]);
return makeLegalIdentifier(
placeholder
.replace("[dir]", path.basename(path.dirname(file)))
.replace("[name]", path.basename(file, path.extname(file)))
.replace("[local]", name)
.replace("[dir]", path.basename(dir))
.replace("[name]", name)
.replace("[local]", local)
.replace(hashRe, hashLen ? hash.slice(0, hashLen) : hash),
);
};
13 changes: 7 additions & 6 deletions src/loaders/postcss/url/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import hasher from "../../../utils/hasher";
import { hashRe } from "../common";

export default (placeholder: string, file: string, source: Uint8Array): string => {
const hash = hasher(`${path.basename(file)}:${Buffer.from(source).toString()}`);
const { dir, name, ext, base } = path.parse(file);
const hash = hasher(`${base}:${Buffer.from(source).toString()}`);
const match = hashRe.exec(placeholder);
const hashLen = match && Number.parseInt(match[1]);
return placeholder
.replace("[dir]", path.basename(path.dirname(file)))
.replace("[name]", path.basename(file, path.extname(file)))
.replace("[extname]", path.extname(file))
.replace(".[ext]", path.extname(file))
.replace("[ext]", path.extname(file).slice(1))
.replace("[dir]", path.basename(dir))
.replace("[name]", name)
.replace("[extname]", ext)
.replace(".[ext]", ext)
.replace("[ext]", ext.slice(1))
.replace(hashRe, hashLen ? hash.slice(0, hashLen) : hash.slice(0, 8));
};
26 changes: 13 additions & 13 deletions src/utils/sourcemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,27 @@ export const stripMap = (code: string): string =>
code.replace(mapBlockRe, "").replace(mapLineRe, "");

class MapModifier {
readonly #map?: RawSourceMap;
private readonly map?: RawSourceMap;

constructor(map?: string | RawSourceMap) {
if (typeof map === "string")
try {
this.#map = JSON.parse(map) as RawSourceMap;
this.map = JSON.parse(map) as RawSourceMap;
} catch {
/* noop */
}
else this.#map = map;
else this.map = map;
}

modify(f: (m: RawSourceMap) => void): this {
if (!this.#map) return this;
f(this.#map);
if (!this.map) return this;
f(this.map);
return this;
}

modifySources(op: (source: string) => string): this {
if (!this.#map) return this;
if (this.#map.sources) this.#map.sources = this.#map.sources.map(s => op(s));
if (!this.map) return this;
if (this.map.sources) this.map.sources = this.map.sources.map(s => op(s));
return this;
}

Expand All @@ -65,17 +65,17 @@ class MapModifier {
}

toObject(): RawSourceMap | undefined {
return this.#map;
return this.map;
}

toString(): string | undefined {
if (!this.#map) return this.#map;
return JSON.stringify(this.#map);
if (!this.map) return this.map;
return JSON.stringify(this.map);
}

async toConsumer(): Promise<BasicSourceMapConsumer | undefined> {
if (!this.#map) return this.#map;
return new SourceMapConsumer(this.#map);
if (!this.map) return this.map;
return new SourceMapConsumer(this.map);
}

toCommentData(): string {
Expand All @@ -86,7 +86,7 @@ class MapModifier {
}

toCommentFile(fileName: string): string {
if (!this.#map) return "";
if (!this.map) return "";
return `\n/*# sourceMappingURL=${fileName} */`;
}
}
Expand Down

0 comments on commit df1d1ee

Please sign in to comment.