forgecss 0.3.0 → 0.5.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/index.d.ts CHANGED
@@ -2,17 +2,16 @@ export type ForgeCSSOptions = {
2
2
  inventoryFiles?: string[];
3
3
  usageFiles?: string[];
4
4
  usageAttributes?: string[];
5
- mapping?: {
6
- queries?: {
7
- [key: string]: string
8
- };
5
+ breakpoints?: {
6
+ [key: string]: string;
9
7
  };
8
+ verbose?: boolean;
10
9
  };
11
10
 
12
11
  export type ForgeInstance = {
13
- parseDirectory: (options: { dir: string; output?: string }) => Promise<string>;
14
- parseFile: (options: { file: string; output?: string }) => Promise<string>;
15
- parse: (options: { css: string; html?: string; jsx?: string; output?: string }) => Promise<string>;
12
+ parseDirectory: (options: { dir: string; output?: string; watch?: boolean }) => Promise<string>;
13
+ parseFile: (options: { file: string; output?: string; watch?: boolean }) => Promise<string>;
14
+ parse: (options: { css: string; html?: string; jsx?: string; output?: string; }) => Promise<string>;
16
15
  };
17
16
 
18
17
  declare function ForgeCSS(options?: ForgeCSSOptions): ForgeInstance;
package/index.js CHANGED
@@ -1,24 +1,27 @@
1
+ import path from 'path';
1
2
  import { writeFile } from "fs/promises";
2
3
  import getAllFiles from "./lib/getAllFiles.js";
3
4
  import { extractStyles, invalidateInvetory } from "./lib/inventory.js";
4
5
  import { invalidateUsageCache, findUsages } from "./lib/usages.js";
5
6
  import { generateOutputCSS } from "./lib/generator.js";
7
+ import chokidar from "chokidar";
6
8
 
7
9
  const DEFAULT_OPTIONS = {
8
10
  inventoryFiles: ["css", "less", "scss"],
9
11
  usageFiles: ["html", "jsx", "tsx"],
10
12
  usageAttributes: ["class", "className"],
11
- mapping: {
12
- queries: {}
13
- }
13
+ breakpoints: {},
14
+ verbose: true
14
15
  };
15
16
 
16
17
  export default function ForgeCSS(options) {
17
18
  const config = { ...DEFAULT_OPTIONS };
18
19
 
19
- config.mapping = {
20
- queries: Object.assign({}, DEFAULT_OPTIONS.mapping.queries, options?.mapping?.queries ?? {})
21
- };
20
+ config.breakpoints = Object.assign({}, DEFAULT_OPTIONS.breakpoints, options?.breakpoints ?? {});
21
+ config.inventoryFiles = options?.inventoryFiles ?? DEFAULT_OPTIONS.inventoryFiles;
22
+ config.usageFiles = options?.usageFiles ?? DEFAULT_OPTIONS.usageFiles;
23
+ config.usageAttributes = options?.usageAttributes ?? DEFAULT_OPTIONS.usageAttributes;
24
+ config.verbose = options?.verbose ?? DEFAULT_OPTIONS.verbose;
22
25
 
23
26
  async function result(output) {
24
27
  try {
@@ -26,15 +29,32 @@ export default function ForgeCSS(options) {
26
29
  if (output) {
27
30
  await writeFile(output, `/* ForgeCSS autogenerated file */\n${css}`, "utf-8");
28
31
  }
32
+ console.log("forgecss: Output CSS generated successfully.");
29
33
  return css;
30
34
  } catch (err) {
31
35
  console.error(`forgecss: error generating output CSS: ${err}`);
32
36
  }
33
37
  return null;
34
38
  }
39
+ function runWatcher(what, output, callback) {
40
+ const watcher = chokidar.watch(what, {
41
+ persistent: true,
42
+ ignoreInitial: true,
43
+ ignored: (p, stats) => output && path.resolve(p) === path.resolve(output)
44
+ });
45
+ watcher.on("change", async (filePath) => {
46
+ if (config.verbose) {
47
+ console.log(`forgecss: Detected change in ${filePath}`);
48
+ }
49
+ callback();
50
+ });
51
+ if (config.verbose) {
52
+ console.log("forgecss: Watch mode enabled. Listening for file changes...");
53
+ }
54
+ }
35
55
 
36
56
  return {
37
- async parseDirectory({ dir, output = null }) {
57
+ async parseDirectory({ dir, output = null, watch = false }) {
38
58
  if (!dir) {
39
59
  throw new Error('forgecss: parseDirectory requires "dir" as an argument.');
40
60
  }
@@ -56,10 +76,13 @@ export default function ForgeCSS(options) {
56
76
  } catch (err) {
57
77
  console.error(`forgecss: error extracting usages`, err);
58
78
  }
79
+ watch && runWatcher(dir, output, () => {
80
+ this.parseDirectory({ dir, output, watch: false });
81
+ });
59
82
  // generating the output CSS
60
83
  return result(output);
61
84
  },
62
- async parseFile({ file, output = null }) {
85
+ async parseFile({ file, output = null, watch = false }) {
63
86
  if (!file) {
64
87
  throw new Error('forgecss: parseFile requires "file" as an argument.');
65
88
  }
@@ -81,6 +104,9 @@ export default function ForgeCSS(options) {
81
104
  } catch (err) {
82
105
  console.error(`forgecss: error extracting usages.`, err);
83
106
  }
107
+ watch && runWatcher(file, output, () => {
108
+ this.parseFile({ file, output, watch: false });
109
+ });
84
110
  // generating the output CSS
85
111
  return result(output);
86
112
  },
package/lib/generator.js CHANGED
@@ -2,6 +2,7 @@ import { getUsages } from "./usages.js";
2
2
  import arbitraryTransformer from "./transformers/arbitrary.js";
3
3
  import mediaQueryTransformer from "./transformers/mediaQuery.js";
4
4
  import pseudoClassTransformer from "./transformers/pseudo.js";
5
+ import { resolveApplys } from "./inventory.js";
5
6
 
6
7
  export async function generateOutputCSS(config) {
7
8
  const bucket = {};
@@ -27,6 +28,7 @@ export async function generateOutputCSS(config) {
27
28
  }
28
29
  });
29
30
  });
31
+ resolveApplys(bucket);
30
32
  return Object.keys(bucket)
31
33
  .map((key) => {
32
34
  if (bucket[key].rules) {
@@ -1,3 +1,4 @@
1
1
  export function extractStyles(filePath: string, css?: string | null): Promise<void>;
2
2
  export function getStylesByClassName(selector: string): object[];
3
3
  export function invalidateInvetory(): void;
4
+ export function resolveApplys(bucket: object): void
package/lib/inventory.js CHANGED
@@ -19,8 +19,37 @@ export function getStylesByClassName(selector) {
19
19
  }
20
20
  });
21
21
  });
22
+ if (decls.length === 0) {
23
+ console.warn(`forgecss: Warning - no styles found for class "${selector}".`);
24
+ }
22
25
  return decls;
23
26
  }
24
27
  export function invalidateInvetory() {
25
28
  INVENTORY = {};
29
+ }
30
+ export function resolveApplys(bucket) {
31
+ Object.keys(INVENTORY).forEach((filePath) => {
32
+ INVENTORY[filePath].walkRules((rule) => {
33
+ rule.walkDecls((d) => {
34
+ if (d.prop === '--apply') {
35
+ const classesToApply = d.value.split(' ').map(c => c.trim()).filter(Boolean);
36
+ const newRule = postcss.rule({ selector: rule.selector });
37
+ classesToApply.forEach((className) => {
38
+ const styles = getStylesByClassName(className);
39
+ styles.forEach((style) => {
40
+ newRule.append({
41
+ prop: style.prop,
42
+ value: style.value,
43
+ important: style.important
44
+ });
45
+ });
46
+ });
47
+ if (!bucket['_APPLY_']) {
48
+ bucket["_APPLY_"] = postcss.root();
49
+ }
50
+ bucket["_APPLY_"].append(newRule);
51
+ }
52
+ });
53
+ });
54
+ });
26
55
  }
@@ -4,14 +4,14 @@ import { setDeclarations } from "../helpers.js";
4
4
  import {normalizeLabel} from "../../client/fx.js";
5
5
 
6
6
  export default function mediaQueryTransformer(config, label, classes, bucket) {
7
- if (!config?.mapping?.queries[label]) {
7
+ if (!config?.breakpoints[label]) {
8
8
  return false;
9
9
  }
10
10
  if (!bucket[label]) {
11
11
  bucket[label] = {
12
12
  rules: postcss.atRule({
13
13
  name: "media",
14
- params: `all and (${config.mapping.queries[label]})`
14
+ params: `all and (${config.breakpoints[label]})`
15
15
  }),
16
16
  classes: {}
17
17
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgecss",
3
- "version": "0.3.0",
3
+ "version": "0.5.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",