Skip to content

Commit c7177e6

Browse files
committed
chore(dev-utils): Combine all scss files into react-md/dist/_everything.scss
I really only created this file because after switching from `node-sass` to `sass`, my `sassdoc` generator ended up taking 45s+ to compile each example instead of 5-10s. After debugging a bit, I found out the bottleneck is the file resolution IO so I decided to just combine the files myself into a single `react-md/dist/_everything.scss` file which cut down the **total** `sassdoc` generation time to ~55s. It will be interesting to see if this single file import will improve build performance for real world apps, but it'll also require users to be fully migrated to `v3`.
1 parent e0ef881 commit c7177e6

File tree

5 files changed

+207
-2
lines changed

5 files changed

+207
-2
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"dev": "npm-run-all -p watch start",
1515
"setup": "npm-run-all build-dev-utils force-install build \"sandbox --empty\"",
1616
"force-install": "yarn --force",
17+
"combine-styles": "dev-utils combine-styles",
1718
"styles": "dev-utils styles",
1819
"variables": "dev-utils variables",
1920
"run-indexer": "dev-utils doc-index",
@@ -26,7 +27,7 @@
2627
"build-cjs": "tsc -b tsconfig.cjs.json",
2728
"build-var": "tsc -b tsconfig.var.json",
2829
"build-umd": "yarn workspace react-md umd --silent",
29-
"build": "npm-run-all styles build-ejs build-cjs build-var",
30+
"build": "npm-run-all styles combine-styles build-ejs build-cjs build-var",
3031
"typecheck": "tsc -p tsconfig.check.json",
3132
"lint-scripts": "eslint \"packages/*/src/**/*.{ts,tsx,js,jsx}\"",
3233
"lint-styles": "sass-lint -c .sass-lint.yml -v",

packages/dev-utils/src/cli.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { shared } from "./shared";
1616
import { themes } from "./themes";
1717
import { typedoc } from "./typedoc";
1818
import { umd } from "./umd";
19-
import { copyStyles } from "./utils";
19+
import { combineAllFiles, copyStyles } from "./utils";
2020
import { variables } from "./variables";
2121
import { watch } from "./watch";
2222

@@ -56,6 +56,12 @@ createCommand("styles")
5656
)
5757
.action(() => copyStyles());
5858

59+
createCommand("combine-styles")
60+
.description(
61+
"Combines all the .scss files into a single file in the react-md package."
62+
)
63+
.action(() => combineAllFiles());
64+
5965
createCommand("sassdoc")
6066
.description(
6167
"Creates the sassdoc for the documentation site in all scoped packages."

packages/dev-utils/src/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ export const dist = "dist";
2424
export const nonWebpackDist = join(dist, "scss");
2525
export const tempStylesDir = "tempStyles";
2626

27+
export const reactMdDist = join(packagesRoot, "react-md", dist);
28+
export const everythingScss = join(reactMdDist, "_everything.scss");
29+
2730
// common files
2831
export const stylesScss = "styles.scss";
2932
export const scssVariables = "scssVariables.ts";
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
import { existsSync, readFileSync, writeFileSync } from "fs";
2+
import { ensureDirSync } from "fs-extra";
3+
import { difference } from "lodash";
4+
import log from "loglevel";
5+
import { join, sep } from "path";
6+
7+
import {
8+
everythingScss,
9+
packagesRoot,
10+
reactMdDist,
11+
src,
12+
} from "../../constants";
13+
import { format } from "../format";
14+
import { getPackages } from "../packages";
15+
16+
const getFileOrder = (packageName: string): readonly string[] => {
17+
switch (packageName) {
18+
case "theme":
19+
return [
20+
"color-palette",
21+
"color-a11y",
22+
"variables",
23+
"helpers",
24+
"functions",
25+
"mixins",
26+
];
27+
case "icon":
28+
return ["variables", "functions", "mixins", "material-icons"];
29+
case "media":
30+
case "transition":
31+
return ["variables", "mixins"];
32+
case "form":
33+
return [
34+
join("label", "variables"),
35+
join("text-field", "variables"),
36+
join("select", "variables"),
37+
join("toggle", "variables"),
38+
join("slider", "variables"),
39+
"variables",
40+
"functions",
41+
join("slider", "functions"),
42+
join("file-input", "mixins"),
43+
join("label", "mixins"),
44+
join("toggle", "mixins"),
45+
join("slider", "mixins"),
46+
join("text-field", "mixins"),
47+
join("select", "mixins"),
48+
"mixins",
49+
];
50+
default:
51+
return ["variables", "functions", "mixins"];
52+
}
53+
};
54+
55+
const IMPORT_STATEMENT_REGEXP = /^.+('|")(.+)('|");(.*)$/;
56+
57+
const PACKAGE_ORDER = [
58+
"utils",
59+
"theme",
60+
"transition",
61+
"typography",
62+
"elevation",
63+
"divider",
64+
"media",
65+
"icon",
66+
"states",
67+
"overlay",
68+
"tooltip",
69+
"avatar",
70+
"button",
71+
"alert",
72+
"app-bar",
73+
"badge",
74+
"card",
75+
"chip",
76+
"link",
77+
"list",
78+
"expansion-panel",
79+
"dialog",
80+
"sheet",
81+
"menu",
82+
"progress",
83+
"tree",
84+
"table",
85+
"tabs",
86+
"form",
87+
"layout",
88+
];
89+
90+
function assertAllPackages(): void {
91+
const packages = getPackages("scss");
92+
const diff = difference(packages, PACKAGE_ORDER);
93+
if (diff.length) {
94+
process.exit(1);
95+
}
96+
}
97+
98+
const imported: Record<string, boolean> = {};
99+
100+
export function combineAllFiles(): void {
101+
assertAllPackages();
102+
const uses = new Set<string>();
103+
const files: string[] = [];
104+
PACKAGE_ORDER.forEach((packageName) => {
105+
getFileOrder(packageName).forEach((fileName) => {
106+
const packageRoot = join(packagesRoot, packageName, src);
107+
if (fileName.includes(sep)) {
108+
fileName = fileName.replace(sep, `${sep}_`);
109+
} else {
110+
fileName = `_${fileName}`;
111+
}
112+
const filePath = join(packageRoot, `${fileName}.scss`);
113+
114+
imported[filePath] = true;
115+
if (!existsSync(filePath)) {
116+
log.error(`${filePath} does not exist`);
117+
process.exit(1);
118+
}
119+
120+
const contents = readFileSync(filePath, "utf8");
121+
122+
const fileUses = contents.match(/^@use "(.+)";/gm);
123+
const imports = contents.match(/^@import.+$/gm);
124+
files.push(
125+
contents
126+
// remove import and use statements
127+
.replace(/^@(use|import).+$/gm, "")
128+
// remove all comments
129+
.replace(/^\s*\/\/.+$/gm, "")
130+
);
131+
132+
if (fileUses) {
133+
fileUses.forEach((use) => {
134+
uses.add(use);
135+
});
136+
}
137+
138+
if (imports) {
139+
imports.forEach((imp) => {
140+
let importName = imp.replace(IMPORT_STATEMENT_REGEXP, "$2.scss");
141+
if (importName.startsWith("./")) {
142+
importName = join(packageRoot, importName);
143+
} else if (importName.startsWith("../")) {
144+
const [folder] = fileName.split(sep);
145+
importName = join(packageRoot, folder, importName);
146+
}
147+
148+
importName = importName
149+
.replace(/\/([a-z0-9-]+\.scss)/, "/_$1")
150+
.replace(
151+
/~@react-md\/([a-z-]+)\/dist/,
152+
join(packagesRoot, "$1", src)
153+
);
154+
155+
if (!imported[importName]) {
156+
log.error(`imp: "${imp}"`);
157+
log.error(`${importName} needs to be imported before ${filePath}`);
158+
const sorted = Object.keys(imported)
159+
.map((name) => name.replace(packagesRoot, ""))
160+
.sort();
161+
log.error(JSON.stringify(sorted, null, 2));
162+
process.exit(1);
163+
}
164+
});
165+
}
166+
});
167+
});
168+
169+
const contents = `${Array.from(uses).join("\n")}
170+
171+
${files.join("\n")}
172+
`;
173+
const formatted = format(contents, "scss")
174+
// remove extra spaces between variables after comments were removed
175+
.replace(/(\r?\n)+\$/g, "\n$");
176+
177+
ensureDirSync(reactMdDist);
178+
writeFileSync(everythingScss, formatted);
179+
}
180+
181+
let cachedEverythingScss = "";
182+
183+
/**
184+
* Lazy loads the `packages/react-md/dist/_everything.scss` file contents which
185+
* should be used in all the `renderSync` `data` calls to increase build
186+
* performance.
187+
*/
188+
export function getEverythingScss(): string {
189+
if (!cachedEverythingScss) {
190+
cachedEverythingScss = readFileSync(everythingScss, "utf8");
191+
}
192+
193+
return cachedEverythingScss;
194+
}

packages/dev-utils/src/utils/styles/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from "./combineAllFiles";
12
export * from "./createScssVariables";
23
export * from "./getSassdoc";
34
export * from "./helpers";

0 commit comments

Comments
 (0)