Skip to content

Commit 0166309

Browse files
authored
feat: STRF-11834 Consolidate css assembling logic (#117)
1 parent 8366151 commit 0166309

10 files changed

Lines changed: 304 additions & 43 deletions

File tree

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Jira: [JIRA_TOKEN](https://bigcommercecloud.atlassian.net/browse/JIRA_TOKEN)
2+
3+
## What/Why?
4+
<!---
5+
A description about what this pull request implements and its purpose.
6+
Try to be detailed and describe any technical details to simplify the job
7+
of the reviewer and the individual on production support.
8+
--->
9+
10+
## Rollout/Rollback
11+
<!--
12+
Detail how this change will be rolled out. Include reference to any
13+
experiments and how the success will be measured as the experiment is
14+
ramped.
15+
16+
Document rollback procedures. Is rolling back the change as simple as
17+
rolling back an experiment or does it require reverting code? Are there
18+
database migrations that may change our decision to roll forward instead of
19+
back?
20+
-->
21+
22+
## Testing
23+
<!---
24+
Provide as much information as you can about how you tested and how another
25+
Engineer can test your change. Include screenshots, or test run output
26+
where appropriate.
27+
--->

lib/CssAssembler.js

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
const async = require('async');
2+
const fs = require('fs');
3+
const path = require('path');
4+
const upath = require('upath');
5+
6+
const importRegex = /@import\s+?["'](.+?)["'];/g;
7+
8+
/**
9+
* Parses all of the imports for SCSS files and returns a flat object with the file path as the key and
10+
* file content as the value.
11+
*
12+
* @param {Array|string} cssFiles
13+
* @param {string} absolutePath
14+
* @param {string} type - scss | css
15+
* @param options - options object { bundle: boolean }
16+
* @param callback
17+
*/
18+
function assemble(cssFiles, absolutePath, type, options, callback) {
19+
const ret = {};
20+
const ext = `.${type}`;
21+
22+
const cssFilesArr = Array.isArray(cssFiles) ? cssFiles : [cssFiles];
23+
24+
const parseImports = (cssFileAlias, parseImportsCallback) => {
25+
const normalizedCssFileAlias = cssFileAlias.replace(/(\.scss)$/g, '');
26+
const typedCssFilePath = path.join(absolutePath, normalizedCssFileAlias + ext);
27+
const rawCssFilePath = path.join(absolutePath, normalizedCssFileAlias + '.css');
28+
const cssFileAliasDir = path.parse(normalizedCssFileAlias).dir;
29+
30+
const fileParts = path.parse(typedCssFilePath);
31+
const underscoredFilePath = path.join(fileParts.dir, `_${fileParts.base}`);
32+
let filePath;
33+
34+
if (fs.existsSync(typedCssFilePath)) {
35+
filePath = typedCssFilePath;
36+
} else if (fs.existsSync(underscoredFilePath)) {
37+
filePath = underscoredFilePath;
38+
} else if (ext === '.scss' && fs.existsSync(rawCssFilePath)) {
39+
filePath = rawCssFilePath;
40+
} else {
41+
// File doesn't exist, just return to skip it
42+
parseImportsCallback();
43+
return;
44+
}
45+
46+
fs.readFile(filePath, { encoding: 'utf-8' }, (err, content) => {
47+
const matches = [];
48+
let match;
49+
let importCssFileAlias;
50+
51+
if (!err) {
52+
// Ensure all import paths are Unix paths for prod compat.
53+
const cssFile = options.bundle
54+
? upath.toUnix(normalizedCssFileAlias + ext)
55+
: normalizedCssFileAlias + ext;
56+
57+
ret[cssFile] = content;
58+
match = importRegex.exec(content);
59+
60+
while (match !== null) {
61+
[, importCssFileAlias] = match;
62+
if (cssFileAliasDir) {
63+
importCssFileAlias = path.join(cssFileAliasDir, importCssFileAlias);
64+
}
65+
66+
if (!ret[importCssFileAlias + ext]) {
67+
matches.push(importCssFileAlias);
68+
}
69+
70+
match = importRegex.exec(content);
71+
}
72+
}
73+
74+
async.each(matches, parseImports, parseImportsCallback);
75+
});
76+
};
77+
78+
async.map(
79+
cssFilesArr,
80+
(cssFile, mapCallback) => {
81+
const cssFileParts = path.parse(cssFile);
82+
const cssFileAlias = path.join(cssFileParts.dir, cssFileParts.name);
83+
84+
parseImports(cssFileAlias, mapCallback);
85+
},
86+
(err) => {
87+
if (err) {
88+
callback(err);
89+
return;
90+
}
91+
92+
callback(null, ret);
93+
},
94+
);
95+
}
96+
97+
module.exports = {
98+
assemble,
99+
};

lib/styles.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
const autoprefixer = require('autoprefixer');
22
const postcss = require('postcss');
3+
const { promisify } = require('util');
4+
35
const ScssCompiler = require("./ScssCompiler");
46
const StylesheetsFileResolver = require('./StylesheetsFileResolver');
7+
const CssAssembler = require('./CssAssembler');
58

69
class StencilStyles {
710
/**
@@ -33,9 +36,16 @@ class StencilStyles {
3336
}
3437

3538
autoPrefix(css, autoprefixerOptions) {
39+
if (!css || (typeof css !== 'string' && !(css instanceof Buffer))) {
40+
return "";
41+
}
42+
if (!autoprefixerOptions || Object.keys(autoprefixerOptions).length === 0) {
43+
return css;
44+
}
3645
const payload = typeof css === 'string' || css instanceof Buffer ? css : '';
3746
let output = css;
3847

48+
// todo check if autoprefixerOptions is not an empty object
3949
try {
4050
output = postcss([autoprefixer(autoprefixerOptions)]).process(payload).css;
4151
} catch (e) {
@@ -57,6 +67,11 @@ class StencilStyles {
5767
const resolver = new StylesheetsFileResolver(themePath);
5868
const files = await resolver.get();
5969
return files;
70+
}
71+
72+
async assembleCssFiles(cssFiles, absolutePath, type, options) {
73+
const assemble = promisify(CssAssembler.assemble);
74+
return assemble(cssFiles, absolutePath, type, options);
6075
}
6176
}
6277

package-lock.json

Lines changed: 62 additions & 41 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,20 @@
2828
"lib/"
2929
],
3030
"dependencies": {
31+
"async": "^3.2.6",
3132
"autoprefixer": "^10.4.20",
3233
"cheerio": "^1.0.0-rc.12",
3334
"lodash": "^4.17.21",
3435
"node-sass": "^9.0.0",
3536
"postcss": "^8.4.45",
3637
"recursive-readdir": "^2.2.3",
37-
"sass": "^1.58.0"
38+
"sass": "^1.58.0",
39+
"upath": "^2.0.1"
3840
},
3941
"devDependencies": {
4042
"@hapi/code": "^8.0.2",
4143
"@hapi/lab": "^23.0.0",
4244
"eslint": "^7.20.0",
43-
"sinon": "^18.0.0"
45+
"sinon": "^19.0.2"
4446
}
4547
}

0 commit comments

Comments
 (0)