forgecss 0.1.6 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli.js CHANGED
@@ -7,13 +7,13 @@ import { program } from "commander";
7
7
  import ForgeCSS from './index.js';
8
8
  import chokidar from "chokidar";
9
9
 
10
- program.option("--config", "Path to forgecss config file", process.cwd() + "/forgecss.config.js");
11
- program.option("--watch", "Enable watch mode", false);
12
- program.option("--verbose", "Enable watch mode", false);
10
+ program.option("-c, --config <string>,", "Path to forgecss config file", process.cwd() + "/forgecss.config.js");
11
+ program.option("-w, --watch", "Enable watch mode", false);
12
+ program.option("-v, --verbose", "Enable watch mode", false);
13
13
  program.parse();
14
14
 
15
15
  const options = program.opts();
16
- let config = null;
16
+ let config = null, instance = null;
17
17
 
18
18
  if (!fs.existsSync(options.config)) {
19
19
  throw new Error(`forgecss: Config file not found at ${options.config}. Check the --config option.`);
@@ -24,22 +24,24 @@ async function loadConfig(configPath) {
24
24
  const fileUrl = pathToFileURL(abs).href;
25
25
 
26
26
  const mod = await import(fileUrl);
27
- return mod.default ?? mod; // support both default and named export
27
+ return mod.default ?? mod;
28
28
  }
29
29
  async function runForgeCSS(lookAtPath = null) {
30
30
  if (!config) {
31
31
  // The very first run
32
32
  config = await loadConfig(options.config);
33
+ if (!config.dir) {
34
+ throw new Error('forgecss: missing "dir" in configuration.');
35
+ }
36
+ if (!config.output) {
37
+ throw new Error('forgecss: missing "output" in configuration.');
38
+ }
39
+ instance = ForgeCSS(config);
33
40
  if (options.watch) {
34
- const watcher = chokidar.watch(config.source, {
41
+ const watcher = chokidar.watch(config.dir, {
35
42
  persistent: true,
36
43
  ignoreInitial: true,
37
- ignored: (p, stats) => {
38
- if (path.resolve(p) === path.resolve(config.output)) {
39
- return true;
40
- }
41
- return false;
42
- }
44
+ ignored: (p, stats) => path.resolve(p) === path.resolve(config.output)
43
45
  });
44
46
  watcher.on("change", async (filePath) => {
45
47
  if (options.verbose) {
@@ -52,11 +54,14 @@ async function runForgeCSS(lookAtPath = null) {
52
54
  }
53
55
  }
54
56
  }
55
- ForgeCSS(config).parse(lookAtPath).then(() => {
56
- if (options.verbose) {
57
- console.log(`forgecss: CSS generation at ${config.output} completed.`);
58
- }
59
- });
57
+ if (lookAtPath) {
58
+ instance.parseFile(lookAtPath, config.output);
59
+ } else {
60
+ instance.parseDirectory(config.dir, config.output);
61
+ }
62
+ if (options.verbose) {
63
+ console.log(`forgecss: ${config.output} generated successfully.`);
64
+ }
60
65
  }
61
66
 
62
67
  runForgeCSS();
package/index.d.ts CHANGED
@@ -1,18 +1,20 @@
1
1
  export type ForgeCSSOptions = {
2
- source: string;
3
- stylesMatch?: string[];
4
- declarationsMatch?: string[];
5
- declarationsMatchAttributes?: string[];
6
- mapping: {
7
- queries: {
8
- [key: string]: {
9
- query: string;
10
- };
2
+ inventoryFiles?: string[];
3
+ usageFiles?: string[];
4
+ usageAttributes?: string[];
5
+ mapping?: {
6
+ queries?: {
7
+ [key: string]: string
11
8
  };
12
9
  };
13
- output: string;
14
10
  };
15
11
 
16
- export default function forgecss(options?: ForgeCSSOptions): {
17
- parse: (filePathToSpecificFile?: string) => Promise<void>;
18
- };
12
+ export type ForgeInstance = {
13
+ parseDirectory: (directoryPath: string, outputFile?: string) => Promise<string>;
14
+ parseFile: (filePath: string, outputFile?: string) => Promise<string>;
15
+ parse: (css: string, html: string, outputFile?: string) => Promise<string>;
16
+ };
17
+
18
+ declare function ForgeCSS(options?: ForgeCSSOptions): ForgeInstance;
19
+
20
+ export default ForgeCSS;
package/index.js CHANGED
@@ -1,72 +1,111 @@
1
+ import { writeFile } from "fs/promises";
1
2
  import getAllFiles from "./lib/getAllFiles.js";
2
- import { extractStyles } from "./lib/styles.js";
3
- import { deleteDeclarations, extractDeclarations } from "./lib/processFile.js";
3
+ import { extractStyles, invalidateInvetory } from "./lib/inventory.js";
4
+ import { invalidateUsageCache, findUsages } from "./lib/processor.js";
4
5
  import { generateOutputCSS } from "./lib/generator.js";
5
6
 
6
7
  const DEFAULT_OPTIONS = {
7
- source: null,
8
- stylesMatch: ['css', 'less', 'scss'],
9
- declarationsMatch: ['html', 'jsx', 'tsx'],
10
- declarationsMatchAttributes: ['class', 'className'],
8
+ inventoryFiles: ["css", "less", "scss"],
9
+ usageFiles: ["html", "jsx", "tsx"],
10
+ usageAttributes: ["class", "className"],
11
11
  mapping: {
12
12
  queries: {}
13
- },
14
- output: null
13
+ }
15
14
  };
16
15
 
17
- export default function forgecss(options = { source: null, output: null, mapping: {} }) {
16
+ export default function ForgeCSS(options) {
18
17
  const config = { ...DEFAULT_OPTIONS };
19
18
 
20
- config.source = options.source || DEFAULT_OPTIONS.source;
21
- config.mapping = Object.assign({}, DEFAULT_OPTIONS.mapping, options.mapping || {});
22
- config.output = options.output || DEFAULT_OPTIONS.output;
19
+ config.mapping = {
20
+ queries: Object.assign({}, DEFAULT_OPTIONS.mapping.queries, options?.mapping?.queries ?? {})
21
+ };
23
22
 
24
- if (!config.source) {
25
- throw new Error('forgecss: "source" option is required.');
26
- }
27
- if (!config.output) {
28
- throw new Error('forgecss: "output" option is required.');
23
+ async function result(output) {
24
+ try {
25
+ const css = await generateOutputCSS(config);
26
+ if (output) {
27
+ await writeFile(output, `/* ForgeCSS autogenerated file */\n${css}`, "utf-8");
28
+ }
29
+ return css;
30
+ } catch (err) {
31
+ console.error(`forgecss: error generating output CSS: ${err}`);
32
+ }
33
+ return null;
29
34
  }
30
35
 
31
36
  return {
32
- async parse(lookAtPath = null) {
33
- // fetching the styles
37
+ async parseDirectory(dir, output = null) {
38
+ if (!dir) {
39
+ throw new Error('forgecss: parseDirectory requires "dir" as an argument.');
40
+ }
34
41
  try {
35
- if (lookAtPath) {
36
- if (config.stylesMatch.includes(lookAtPath.split(".").pop().toLowerCase())) {
37
- await extractStyles(lookAtPath);
38
- }
39
- } else {
40
- let files = await getAllFiles(config.source, config.stylesMatch);
41
- for (let file of files) {
42
- await extractStyles(file);
43
- }
42
+ // filling the inventory
43
+ let files = await getAllFiles(dir, config.inventoryFiles);
44
+ for (let file of files) {
45
+ await extractStyles(file);
44
46
  }
45
47
  } catch (err) {
46
- console.error(`forgecss: error extracting styles: ${err}`);
48
+ console.error(`forgecss: error extracting styles.`, err);
47
49
  }
48
- // fetching the declarations
50
+ // finding the usages
49
51
  try {
50
- if (lookAtPath) {
51
- if (config.declarationsMatch.includes(lookAtPath.split(".").pop().toLowerCase())) {
52
- deleteDeclarations(lookAtPath);
53
- await extractDeclarations(lookAtPath);
54
- }
55
- } else {
56
- let files = await getAllFiles(config.source, config.declarationsMatch);
57
- for (let file of files) {
58
- await extractDeclarations(file);
59
- }
52
+ let files = await getAllFiles(dir, config.usageFiles);
53
+ for (let file of files) {
54
+ await findUsages(file);
60
55
  }
61
56
  } catch (err) {
62
- console.error(`forgecss: error extracting declarations: ${err}`);
57
+ console.error(`forgecss: error extracting usages`, err);
63
58
  }
64
59
  // generating the output CSS
60
+ return result(output);
61
+ },
62
+ async parseFile(file, output = null) {
63
+ if (!file) {
64
+ throw new Error('forgecss: parseFile requires "file" as an argument.');
65
+ }
66
+ const ext = file.split(".").pop().toLowerCase();
67
+ // filling the inventory
68
+ try {
69
+ if (config.inventoryFiles.includes(ext)) {
70
+ await extractStyles(file);
71
+ }
72
+ } catch (err) {
73
+ console.error(`forgecss: error extracting styles.`, err);
74
+ }
75
+ // finding the usages
76
+ try {
77
+ if (config.usageFiles.includes(ext)) {
78
+ invalidateUsageCache(file);
79
+ await findUsages(file);
80
+ }
81
+ } catch (err) {
82
+ console.error(`forgecss: error extracting usages.`, err);
83
+ }
84
+ // generating the output CSS
85
+ return result(output);
86
+ },
87
+ async parse(css, html, output = null) {
88
+ if (!css) {
89
+ throw new Error('forgecss: parse requires "css" as an argument.');
90
+ }
91
+ if (!html) {
92
+ throw new Error('forgecss: parse requires "html" as an argument.');
93
+ }
94
+ invalidateInvetory();
95
+ invalidateUsageCache();
96
+ // filling the inventory
97
+ try {
98
+ await extractStyles("styles.css", css);
99
+ } catch (err) {
100
+ console.error(`forgecss: error extracting styles.`, err);
101
+ }
102
+ // finding the usages
65
103
  try {
66
- await generateOutputCSS(config);
104
+ await findUsages("usage.html", html);
67
105
  } catch (err) {
68
- console.error(`forgecss: error generating output CSS: ${err}`);
106
+ console.error(`forgecss: error extracting usages.`, err);
69
107
  }
108
+ return result(output);
70
109
  }
71
110
  };
72
111
  }
package/lib/generator.js CHANGED
@@ -1,25 +1,37 @@
1
- import { writeFile } from "fs/promises";
2
- import { getDeclarations } from "./processFile.js";
3
- import { createMediaStyle } from "./styles.js";
1
+ import { getUsages } from "./processor.js";
2
+ import mediaQueryTransformer from "./transformers/mediaQuery.js";
3
+ import pseudoClassTransformer from "./transformers/pseudo.js";
4
4
 
5
5
  export async function generateOutputCSS(config) {
6
- const cache = {};
7
- const declarations = getDeclarations();
8
- Object.keys(declarations).map((file) => {
9
- Object.keys(declarations[file]).forEach(async (label) => {
6
+ const bucket = {};
7
+ const usages = getUsages();
8
+ Object.keys(usages).map((file) => {
9
+ Object.keys(usages[file]).forEach(async (label) => {
10
10
  try {
11
- createMediaStyle(config, label, declarations[file][label], cache);
11
+ if (mediaQueryTransformer(config, label, usages[file][label], bucket)) {
12
+ return;
13
+ } else if (pseudoClassTransformer(label, usages[file][label], bucket)) {
14
+ return;
15
+ }
12
16
  } catch (err) {
13
- console.error(`Error generating media query for label ${label} in file ${file}: ${err}`);
17
+ console.error(
18
+ `forgecss: Error generating media query for label "${label}" (found in file ${file.replace(
19
+ process.cwd(),
20
+ ""
21
+ )})`,
22
+ err
23
+ );
14
24
  }
15
25
  });
16
26
  });
17
- const result = Object.keys(cache)
18
- .map((label) => cache[label].mq.toString())
27
+ return Object.keys(bucket)
28
+ .map((key) => {
29
+ if (bucket[key].rules) {
30
+ return bucket[key].rules.toString();
31
+ }
32
+ return bucket[key].toString();
33
+ })
34
+ .filter(Boolean)
19
35
  .join("\n");
20
- await writeFile(
21
- config.output,
22
- `/* ForgeCSS autogenerated file */\n${result}`,
23
- "utf-8"
24
- );
25
- }
36
+ }
37
+
@@ -0,0 +1,3 @@
1
+ export function extractStyles(filePath: string, css?: string | null): Promise<void>;
2
+ export function getStylesByClassName(selector: string): object[];
3
+ export function invalidateInvetory(): void;
@@ -0,0 +1,26 @@
1
+ import { readFile } from "fs/promises";
2
+ import postcss from "postcss";
3
+ import safeParser from "postcss-safe-parser";
4
+
5
+ let INVENTORY = {};
6
+
7
+ export async function extractStyles(filePath, css = null) {
8
+ const content = css !== null ? css : await readFile(filePath, 'utf-8');
9
+ INVENTORY[filePath] = postcss.parse(content, { parser: safeParser });
10
+ }
11
+ export function getStylesByClassName(selector) {
12
+ const decls = [];
13
+ Object.keys(INVENTORY).forEach((filePath) => {
14
+ INVENTORY[filePath].walkRules((rule) => {
15
+ if (rule.selectors && rule.selectors.includes(`.${selector}`)) {
16
+ rule.walkDecls((d) => {
17
+ decls.push({ prop: d.prop, value: d.value, important: d.important });
18
+ });
19
+ }
20
+ });
21
+ });
22
+ return decls;
23
+ }
24
+ export function invalidateInvetory() {
25
+ INVENTORY = {};
26
+ }
@@ -0,0 +1,3 @@
1
+ export declare function findUsages(filePath: string, content?: string | null): Promise<void>;
2
+ export declare function invalidateUsageCache(filePath?: string): void;
3
+ export declare function getUsages(): object;
@@ -4,25 +4,26 @@ import { fromHtml } from "hast-util-from-html";
4
4
  import { visit } from "unist-util-visit";
5
5
 
6
6
  const FUNC_NAME = 'fx';
7
- const DECLARATIONS = {};
7
+ let USAGES = {};
8
8
 
9
9
  const { parse } = swc;
10
10
 
11
- export async function extractDeclarations(filePath) {
12
- const extension = filePath.split('.').pop().toLowerCase();
11
+ export async function findUsages(filePath, fileContent = null) {
13
12
  try {
14
- if (DECLARATIONS[filePath]) {
13
+ if (USAGES[filePath]) {
14
+ // already processed
15
15
  return;
16
16
  }
17
- DECLARATIONS[filePath] = {};
18
- const content = await readFile(filePath, "utf-8");
17
+ USAGES[filePath] = {};
18
+ const content = fileContent ? fileContent : await readFile(filePath, "utf-8");
19
+ const extension = filePath.split('.').pop().toLowerCase();
19
20
 
20
21
  // HTML
21
22
  if (extension === "html") {
22
23
  const ast = fromHtml(content);
23
24
  visit(ast, "element", (node) => {
24
25
  if (node.properties.className) {
25
- pushToDeclarations(filePath, node.properties.className.join(' '));
26
+ storeUsage(filePath, node.properties.className.join(' '));
26
27
  }
27
28
  });
28
29
  return;
@@ -34,7 +35,7 @@ export async function extractDeclarations(filePath) {
34
35
  tsx: true,
35
36
  decorators: false
36
37
  });
37
- traverseNode(ast, {
38
+ traverseASTNode(ast, {
38
39
  JSXExpressionContainer(node) {
39
40
  if (node?.expression?.callee?.value === FUNC_NAME && node?.expression?.arguments) {
40
41
  if (node?.expression?.arguments[0]) {
@@ -46,37 +47,44 @@ export async function extractDeclarations(filePath) {
46
47
  value += elem?.cooked || "";
47
48
  });
48
49
  }
49
- pushToDeclarations(filePath, value);
50
+ storeUsage(filePath, value);
50
51
  }
51
52
  }
52
53
  }
53
54
  });
54
55
  } catch (err) {
55
- console.error(`forgecss: error processing file ${filePath}: ${err}`);
56
+ console.error(`forgecss: error processing file ${filePath.replace(process.cwd(), '')}`, err);
56
57
  }
57
58
  }
58
- export function deleteDeclarations(filePath) {
59
- if (DECLARATIONS[filePath]) {
60
- delete DECLARATIONS[filePath];
59
+ export function invalidateUsageCache(filePath) {
60
+ if (!filePath) {
61
+ USAGES = {};
62
+ return;
63
+ }
64
+ if (USAGES[filePath]) {
65
+ delete USAGES[filePath];
61
66
  }
62
67
  }
63
- function pushToDeclarations(filePath, classesString = "") {
68
+ export function getUsages() {
69
+ return USAGES;
70
+ }
71
+ function storeUsage(filePath, classesString = "") {
64
72
  if (classesString) {
65
73
  classesString.split(" ").forEach((part) => {
66
74
  if (part.indexOf(":") > -1) {
67
75
  let [label, classes] = part.split(":");
68
76
  classes = classes.split(",");
69
77
  classes.forEach((cls) => {
70
- if (!DECLARATIONS[filePath][label]) {
71
- DECLARATIONS[filePath][label] = [];
78
+ if (!USAGES[filePath][label]) {
79
+ USAGES[filePath][label] = [];
72
80
  }
73
- DECLARATIONS[filePath][label].push(cls);
81
+ USAGES[filePath][label].push(cls);
74
82
  });
75
83
  }
76
84
  });
77
85
  }
78
86
  }
79
- function traverseNode(node, visitors, stack = []) {
87
+ function traverseASTNode(node, visitors, stack = []) {
80
88
  if (!node || typeof node.type !== "string") {
81
89
  return;
82
90
  }
@@ -95,23 +103,20 @@ function traverseNode(node, visitors, stack = []) {
95
103
  child.forEach((c) => {
96
104
  if (c) {
97
105
  if (typeof c.type === "string") {
98
- traverseNode(c, visitors, [node].concat(stack));
106
+ traverseASTNode(c, visitors, [node].concat(stack));
99
107
  } else if (c?.expression && typeof c.expression.type === "string") {
100
- traverseNode(c.expression, visitors, [node].concat(stack));
108
+ traverseASTNode(c.expression, visitors, [node].concat(stack));
101
109
  } else if (c?.callee && typeof c.callee.type === "string") {
102
- traverseNode(c.callee, visitors, [node].concat(stack));
110
+ traverseASTNode(c.callee, visitors, [node].concat(stack));
103
111
  } else if (c?.left && typeof c.left.type === "string") {
104
- traverseNode(c.left, visitors, [node].concat(stack));
112
+ traverseASTNode(c.left, visitors, [node].concat(stack));
105
113
  } else if (c?.right && typeof c.right.type === "string") {
106
- traverseNode(c.right, visitors, [node].concat(stack));
114
+ traverseASTNode(c.right, visitors, [node].concat(stack));
107
115
  }
108
116
  }
109
117
  });
110
118
  } else if (child && typeof child.type === "string") {
111
- traverseNode(child, visitors, [node].concat(stack));
119
+ traverseASTNode(child, visitors, [node].concat(stack));
112
120
  }
113
121
  }
114
- }
115
- export function getDeclarations() {
116
- return DECLARATIONS;
117
122
  }
@@ -0,0 +1,44 @@
1
+ import postcss from "postcss";
2
+
3
+ import { getStylesByClassName } from "../inventory.js";
4
+
5
+ export default function mediaQueryTransformer(config, label, selectors, bucket) {
6
+ if (!config?.mapping?.queries[label]) {
7
+ return false;
8
+ }
9
+ if (!bucket[label]) {
10
+ bucket[label] = {
11
+ rules: postcss.atRule({
12
+ name: "media",
13
+ params: `all and (${config.mapping.queries[label]})`
14
+ }),
15
+ classes: {}
16
+ };
17
+ }
18
+ const rules = bucket[label].rules;
19
+ selectors.forEach((selector) => {
20
+ const prefixedSelector = `.${label}_${selector}`;
21
+ if (bucket[label].classes[prefixedSelector]) {
22
+ return;
23
+ }
24
+ bucket[label].classes[prefixedSelector] = true; // caching
25
+ const rule = postcss.rule({ selector: prefixedSelector });
26
+ const decls = getStylesByClassName(selector);
27
+ if (decls.length === 0) {
28
+ console.warn(`forgecss: no styles found for class ".${selector}" used in media query "${label}"`);
29
+ delete bucket[label];
30
+ return;
31
+ }
32
+ decls.forEach((d) => {
33
+ rule.append(
34
+ postcss.decl({
35
+ prop: d.prop,
36
+ value: d.value,
37
+ important: d.important
38
+ })
39
+ );
40
+ });
41
+ rules.append(rule);
42
+ });
43
+ return true;
44
+ }
@@ -0,0 +1,57 @@
1
+ import postcss from "postcss";
2
+ import { getStylesByClassName } from "../inventory.js";
3
+
4
+ const ALLOWED_PSEUDO_CLASSES = [
5
+ "hover",
6
+ "active",
7
+ "focus",
8
+ "focus-visible",
9
+ "focus-within",
10
+ "disabled",
11
+ "enabled",
12
+ "read-only",
13
+ "read-write",
14
+ "checked",
15
+ "indeterminate",
16
+ "valid",
17
+ "invalid",
18
+ "required",
19
+ "optional",
20
+ "in-range",
21
+ "out-of-range",
22
+ "placeholder-shown",
23
+ "autofill",
24
+ "user-invalid"
25
+ ];
26
+
27
+ export default function pseudoClassTransformer(label, selectors, bucket) {
28
+ if (!ALLOWED_PSEUDO_CLASSES.includes(label)) {
29
+ return false;
30
+ }
31
+ const root = postcss.root();
32
+ selectors.forEach((selector) => {
33
+ const key = `${label}_${selector}`;
34
+ if (bucket[key]) {
35
+ // already have that
36
+ return;
37
+ }
38
+ const rule = postcss.rule({ selector: `.${key}:${label}` });
39
+ const decls = getStylesByClassName(selector);
40
+ if (decls.length === 0) {
41
+ console.warn(`forgecss: no styles found for class ".${selector}" used in pseudo class "${label}"`);
42
+ return;
43
+ }
44
+ decls.forEach((d) => {
45
+ rule.append(
46
+ postcss.decl({
47
+ prop: d.prop,
48
+ value: d.value,
49
+ important: d.important
50
+ })
51
+ );
52
+ });
53
+ root.append(rule);
54
+ bucket[key] = root;
55
+ });
56
+ return true;
57
+ }
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "forgecss",
3
- "version": "0.1.6",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "ForgeCSS turns strings into fully generated responsive CSS using a custom DSL.",
6
6
  "author": "Krasimir Tsonev",
7
7
  "main": "index.js",
8
8
  "scripts": {
9
- "build": "node ./scripts/build.js"
9
+ "build": "node ./scripts/build.js",
10
+ "test": "node ./tests/run.js"
10
11
  },
11
12
  "repository": {
12
13
  "type": "git",
package/tsconfig.json ADDED
@@ -0,0 +1,3 @@
1
+ {
2
+ "include": ["lib/**/*", "index.d.ts", "**/*.js"],
3
+ }
package/lib/styles.js DELETED
@@ -1,61 +0,0 @@
1
- import { readFile } from "fs/promises";
2
- import postcss from "postcss";
3
- import safeParser from "postcss-safe-parser";
4
-
5
- const STYLES = {};
6
-
7
- export async function extractStyles(filePath) {
8
- const content = await readFile(filePath, 'utf-8');
9
- STYLES[filePath] = postcss.parse(content, { parser: safeParser });
10
- }
11
- export function getStylesByClassName(selector) {
12
- const decls = [];
13
- Object.keys(STYLES).forEach((filePath) => {
14
- STYLES[filePath].walkRules((rule) => {
15
- if (rule.selectors && rule.selectors.includes(`.${selector}`)) {
16
- rule.walkDecls((d) => {
17
- decls.push({ prop: d.prop, value: d.value, important: d.important });
18
- });
19
- }
20
- });
21
- });
22
- return decls;
23
- }
24
- export function createMediaStyle(config, label, selectors, cache) {
25
- if (!config.mapping.queries[label]) {
26
- throw new Error(
27
- `Unknown media query label: ${label}. Check app-fe/wwwroot/scripts/lib/generateMediaQueries.js for available mappings.`
28
- );
29
- }
30
- if (!cache[label]) {
31
- cache[label] = {
32
- mq: postcss.atRule({
33
- name: "media",
34
- params: `all and (${config.mapping.queries[label].query})`
35
- }),
36
- classes: {}
37
- };
38
- }
39
- const mq = cache[label].mq;
40
- selectors.forEach((selector) => {
41
- const prefixedSelector = `.${label}_${selector}`;
42
- if (cache[label].classes[prefixedSelector]) { return; }
43
- cache[label].classes[prefixedSelector] = true;
44
- const rule = postcss.rule({ selector: prefixedSelector });
45
- const decls = getStylesByClassName(selector);
46
- if (decls.length === 0) {
47
- console.warn(`Warning: No styles found for class .${selector} used in media query ${label}`);
48
- return;
49
- }
50
- decls.forEach((d) => {
51
- rule.append(
52
- postcss.decl({
53
- prop: d.prop,
54
- value: d.value,
55
- important: d.important
56
- })
57
- );
58
- });
59
- mq.append(rule);
60
- });
61
- }