forgecss 0.10.0 → 0.11.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 +34 -10
- package/dist/forgecss.min.js +13 -13
- package/index.js +8 -8
- package/lib/helpers.js +66 -0
- package/lib/inventory.js +4 -3
- package/lib/usages.js +8 -68
- package/package.json +1 -1
- package/standalone/forgecss.js +4 -4
- package/standalone/lib/inventory.js +0 -59
- package/standalone/lib/usages.js +0 -30
package/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { extractStyles, getStylesByClassName, invalidateInventory, resolveApplys
|
|
|
6
6
|
import { invalidateUsageCache, findUsages, getUsages } from "./lib/usages.js";
|
|
7
7
|
import { astToRules, rulesToCSS } from './lib/forge-lang/Compiler.js';
|
|
8
8
|
import { toAST } from './lib/forge-lang/Parser.js';
|
|
9
|
-
import { getAllFiles } from './lib/helpers.js';
|
|
9
|
+
import { getAllFiles, JSXParser, readFileContent } from './lib/helpers.js';
|
|
10
10
|
|
|
11
11
|
const DEFAULT_OPTIONS = {
|
|
12
12
|
inventoryFiles: ["css", "less", "scss"],
|
|
@@ -47,7 +47,7 @@ export default function ForgeCSS(options) {
|
|
|
47
47
|
await writeFile(output, `/* ForgeCSS auto-generated file */\n${css}`, "utf-8");
|
|
48
48
|
}
|
|
49
49
|
if (config.verbose) {
|
|
50
|
-
console.log("forgecss:
|
|
50
|
+
console.log("forgecss: output CSS generated successfully.");
|
|
51
51
|
}
|
|
52
52
|
return css;
|
|
53
53
|
} catch (err) {
|
|
@@ -83,7 +83,7 @@ export default function ForgeCSS(options) {
|
|
|
83
83
|
// filling the inventory
|
|
84
84
|
let files = await getAllFiles(dir, config.inventoryFiles);
|
|
85
85
|
for (let file of files) {
|
|
86
|
-
await
|
|
86
|
+
extractStyles(file, await readFileContent(file));
|
|
87
87
|
}
|
|
88
88
|
} catch (err) {
|
|
89
89
|
console.error(`forgecss: error extracting styles.`, err);
|
|
@@ -92,7 +92,7 @@ export default function ForgeCSS(options) {
|
|
|
92
92
|
try {
|
|
93
93
|
let files = await getAllFiles(dir, config.usageFiles);
|
|
94
94
|
for (let file of files) {
|
|
95
|
-
await findUsages(file);
|
|
95
|
+
await findUsages(file, await readFileContent(file), JSXParser);
|
|
96
96
|
}
|
|
97
97
|
} catch (err) {
|
|
98
98
|
console.error(`forgecss: error extracting usages`, err);
|
|
@@ -111,7 +111,7 @@ export default function ForgeCSS(options) {
|
|
|
111
111
|
// filling the inventory
|
|
112
112
|
try {
|
|
113
113
|
if (config.inventoryFiles.includes(ext)) {
|
|
114
|
-
await
|
|
114
|
+
extractStyles(file, await readFileContent(file));
|
|
115
115
|
}
|
|
116
116
|
} catch (err) {
|
|
117
117
|
console.error(`forgecss: error extracting styles.`, err);
|
|
@@ -120,7 +120,7 @@ export default function ForgeCSS(options) {
|
|
|
120
120
|
try {
|
|
121
121
|
if (config.usageFiles.includes(ext)) {
|
|
122
122
|
invalidateUsageCache(file);
|
|
123
|
-
await findUsages(file);
|
|
123
|
+
await findUsages(file, await readFileContent(file), JSXParser);
|
|
124
124
|
}
|
|
125
125
|
} catch (err) {
|
|
126
126
|
console.error(`forgecss: error extracting usages.`, err);
|
|
@@ -142,7 +142,7 @@ export default function ForgeCSS(options) {
|
|
|
142
142
|
invalidateUsageCache();
|
|
143
143
|
// filling the inventory
|
|
144
144
|
try {
|
|
145
|
-
|
|
145
|
+
extractStyles("styles.css", css);
|
|
146
146
|
} catch (err) {
|
|
147
147
|
console.error(`forgecss: error extracting styles.`, err);
|
|
148
148
|
}
|
|
@@ -151,7 +151,7 @@ export default function ForgeCSS(options) {
|
|
|
151
151
|
if (html) {
|
|
152
152
|
await findUsages("usage.html", html);
|
|
153
153
|
} else if (jsx) {
|
|
154
|
-
await findUsages("usage.jsx", jsx);
|
|
154
|
+
await findUsages("usage.jsx", jsx, JSXParser);
|
|
155
155
|
}
|
|
156
156
|
} catch (err) {
|
|
157
157
|
console.error(`forgecss: error extracting usages.`, err);
|
package/lib/helpers.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import swc from "@swc/core";
|
|
1
2
|
import fs from "fs/promises";
|
|
2
3
|
import path from "path";
|
|
3
4
|
|
|
5
|
+
const FUNC_NAME = "fx";
|
|
6
|
+
const { parse } = swc;
|
|
7
|
+
|
|
4
8
|
export async function getAllFiles(dir, matchFiles) {
|
|
5
9
|
const result = [];
|
|
6
10
|
const stack = [dir];
|
|
@@ -28,3 +32,65 @@ export async function getAllFiles(dir, matchFiles) {
|
|
|
28
32
|
|
|
29
33
|
return result;
|
|
30
34
|
}
|
|
35
|
+
export function readFileContent(filePath) {
|
|
36
|
+
return fs.readFile(filePath, "utf-8");
|
|
37
|
+
}
|
|
38
|
+
export async function JSXParser(content, USAGES, filePath) {
|
|
39
|
+
const ast = await parse(content, {
|
|
40
|
+
syntax: "typescript",
|
|
41
|
+
tsx: true,
|
|
42
|
+
decorators: false
|
|
43
|
+
});
|
|
44
|
+
function traverseASTNode(node, visitors, stack = []) {
|
|
45
|
+
if (!node || typeof node.type !== "string") {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const visitor = visitors[node.type];
|
|
50
|
+
if (visitor) {
|
|
51
|
+
visitor(node, stack);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
for (const key in node) {
|
|
55
|
+
if (!node.hasOwnProperty(key)) continue;
|
|
56
|
+
|
|
57
|
+
const child = node[key];
|
|
58
|
+
|
|
59
|
+
if (Array.isArray(child)) {
|
|
60
|
+
child.forEach((c) => {
|
|
61
|
+
if (c) {
|
|
62
|
+
if (typeof c.type === "string") {
|
|
63
|
+
traverseASTNode(c, visitors, [node].concat(stack));
|
|
64
|
+
} else if (c?.expression && typeof c.expression.type === "string") {
|
|
65
|
+
traverseASTNode(c.expression, visitors, [node].concat(stack));
|
|
66
|
+
} else if (c?.callee && typeof c.callee.type === "string") {
|
|
67
|
+
traverseASTNode(c.callee, visitors, [node].concat(stack));
|
|
68
|
+
} else if (c?.left && typeof c.left.type === "string") {
|
|
69
|
+
traverseASTNode(c.left, visitors, [node].concat(stack));
|
|
70
|
+
} else if (c?.right && typeof c.right.type === "string") {
|
|
71
|
+
traverseASTNode(c.right, visitors, [node].concat(stack));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
} else if (child && typeof child.type === "string") {
|
|
76
|
+
traverseASTNode(child, visitors, [node].concat(stack));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// writeFile(process.cwd() + '/ast.json', JSON.stringify(ast, null, 2), 'utf-8').catch(() => {});
|
|
81
|
+
traverseASTNode(ast, {
|
|
82
|
+
JSXExpressionContainer(node) {
|
|
83
|
+
if (node?.expression?.callee?.value === FUNC_NAME && node?.expression?.arguments) {
|
|
84
|
+
if (node?.expression?.arguments[0]) {
|
|
85
|
+
const arg = node.expression.arguments[0];
|
|
86
|
+
let value = arg?.expression.value;
|
|
87
|
+
if (arg.expression.type === "TemplateLiteral") {
|
|
88
|
+
let quasis = arg.expression.quasis.map((elem) => elem?.cooked || "");
|
|
89
|
+
value = quasis.join("");
|
|
90
|
+
}
|
|
91
|
+
USAGES[filePath].push(value);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
package/lib/inventory.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { readFile } from "fs/promises";
|
|
2
1
|
import postcss from "postcss";
|
|
3
2
|
import safeParser from "postcss-safe-parser";
|
|
4
3
|
|
|
5
4
|
let INVENTORY = {};
|
|
6
5
|
|
|
7
|
-
export
|
|
8
|
-
const content = css !== null ? css : await readFile(filePath, "utf-8");
|
|
6
|
+
export function extractStyles(filePath, content) {
|
|
9
7
|
INVENTORY[filePath] = postcss.parse(content, { parser: safeParser });
|
|
10
8
|
}
|
|
11
9
|
export function getStylesByClassName(selector) {
|
|
@@ -64,3 +62,6 @@ export function resolveApplys() {
|
|
|
64
62
|
});
|
|
65
63
|
return resolvedApplies;
|
|
66
64
|
}
|
|
65
|
+
export function getInventory() {
|
|
66
|
+
return INVENTORY;
|
|
67
|
+
}
|
package/lib/usages.js
CHANGED
|
@@ -1,22 +1,17 @@
|
|
|
1
|
-
import swc from "@swc/core";
|
|
2
|
-
import { readFile, writeFile } from "fs/promises";
|
|
3
1
|
import { fromHtml } from "hast-util-from-html";
|
|
4
2
|
import { visit } from "unist-util-visit";
|
|
5
3
|
|
|
6
|
-
const FUNC_NAME = 'fx';
|
|
7
|
-
let USAGES = {};
|
|
8
4
|
|
|
9
|
-
|
|
5
|
+
let USAGES = {};
|
|
10
6
|
|
|
11
|
-
export async function findUsages(filePath,
|
|
7
|
+
export async function findUsages(filePath, content, JSXParser) {
|
|
12
8
|
try {
|
|
13
9
|
if (USAGES[filePath]) {
|
|
14
10
|
// already processed
|
|
15
11
|
return;
|
|
16
12
|
}
|
|
17
13
|
USAGES[filePath] = [];
|
|
18
|
-
const
|
|
19
|
-
const extension = filePath.split('.').pop().toLowerCase();
|
|
14
|
+
const extension = filePath.split(".").pop().toLowerCase();
|
|
20
15
|
|
|
21
16
|
// HTML
|
|
22
17
|
if (extension === "html") {
|
|
@@ -28,31 +23,12 @@ export async function findUsages(filePath, fileContent = null) {
|
|
|
28
23
|
});
|
|
29
24
|
return;
|
|
30
25
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
tsx: true,
|
|
36
|
-
decorators: false
|
|
37
|
-
});
|
|
38
|
-
// writeFile(process.cwd() + '/ast.json', JSON.stringify(ast, null, 2), 'utf-8').catch(() => {});
|
|
39
|
-
traverseASTNode(ast, {
|
|
40
|
-
JSXExpressionContainer(node) {
|
|
41
|
-
if (node?.expression?.callee?.value === FUNC_NAME && node?.expression?.arguments) {
|
|
42
|
-
if (node?.expression?.arguments[0]) {
|
|
43
|
-
const arg = node.expression.arguments[0];
|
|
44
|
-
let value = arg?.expression.value;
|
|
45
|
-
if (arg.expression.type === "TemplateLiteral") {
|
|
46
|
-
let quasis = arg.expression.quasis.map((elem) => elem?.cooked || "");
|
|
47
|
-
value = quasis.join("");
|
|
48
|
-
}
|
|
49
|
-
USAGES[filePath].push(value);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
});
|
|
26
|
+
if (JSXParser && (extension === "jsx" || extension === "tsx")) {
|
|
27
|
+
await JSXParser(content, USAGES, filePath);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
54
30
|
} catch (err) {
|
|
55
|
-
console.error(`forgecss: error processing file ${filePath.replace(process.cwd(),
|
|
31
|
+
console.error(`forgecss: error processing file ${filePath.replace(process.cwd(), "")}`, err);
|
|
56
32
|
}
|
|
57
33
|
}
|
|
58
34
|
export function invalidateUsageCache(filePath) {
|
|
@@ -66,40 +42,4 @@ export function invalidateUsageCache(filePath) {
|
|
|
66
42
|
}
|
|
67
43
|
export function getUsages() {
|
|
68
44
|
return USAGES;
|
|
69
|
-
}
|
|
70
|
-
function traverseASTNode(node, visitors, stack = []) {
|
|
71
|
-
if (!node || typeof node.type !== "string") {
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const visitor = visitors[node.type];
|
|
76
|
-
if (visitor) {
|
|
77
|
-
visitor(node, stack);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
for (const key in node) {
|
|
81
|
-
if (!node.hasOwnProperty(key)) continue;
|
|
82
|
-
|
|
83
|
-
const child = node[key];
|
|
84
|
-
|
|
85
|
-
if (Array.isArray(child)) {
|
|
86
|
-
child.forEach((c) => {
|
|
87
|
-
if (c) {
|
|
88
|
-
if (typeof c.type === "string") {
|
|
89
|
-
traverseASTNode(c, visitors, [node].concat(stack));
|
|
90
|
-
} else if (c?.expression && typeof c.expression.type === "string") {
|
|
91
|
-
traverseASTNode(c.expression, visitors, [node].concat(stack));
|
|
92
|
-
} else if (c?.callee && typeof c.callee.type === "string") {
|
|
93
|
-
traverseASTNode(c.callee, visitors, [node].concat(stack));
|
|
94
|
-
} else if (c?.left && typeof c.left.type === "string") {
|
|
95
|
-
traverseASTNode(c.left, visitors, [node].concat(stack));
|
|
96
|
-
} else if (c?.right && typeof c.right.type === "string") {
|
|
97
|
-
traverseASTNode(c.right, visitors, [node].concat(stack));
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
} else if (child && typeof child.type === "string") {
|
|
102
|
-
traverseASTNode(child, visitors, [node].concat(stack));
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
45
|
}
|
package/package.json
CHANGED
package/standalone/forgecss.js
CHANGED
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
invalidateInventory,
|
|
5
5
|
resolveApplys,
|
|
6
6
|
getInventory
|
|
7
|
-
} from "
|
|
8
|
-
import { invalidateUsageCache, findUsages, getUsages } from "
|
|
7
|
+
} from "../lib/inventory.js";
|
|
8
|
+
import { invalidateUsageCache, findUsages, getUsages } from "../lib/usages.js";
|
|
9
9
|
import { astToRules, rulesToCSS } from "../lib/forge-lang/Compiler.js";
|
|
10
10
|
import { toAST } from "../lib/forge-lang/Parser.js";
|
|
11
11
|
import fx from '../lib/fx.js'
|
|
@@ -42,7 +42,7 @@ function ForgeCSS(options) {
|
|
|
42
42
|
rules.push(resolveApplys());
|
|
43
43
|
const css = rulesToCSS(rules.filter(Boolean), config);
|
|
44
44
|
if (config.verbose) {
|
|
45
|
-
console.log("forgecss:
|
|
45
|
+
console.log("forgecss: output CSS generated successfully.");
|
|
46
46
|
}
|
|
47
47
|
return css;
|
|
48
48
|
} catch (err) {
|
|
@@ -63,7 +63,7 @@ function ForgeCSS(options) {
|
|
|
63
63
|
invalidateUsageCache();
|
|
64
64
|
// filling the inventory
|
|
65
65
|
try {
|
|
66
|
-
|
|
66
|
+
extractStyles("styles.css", css);
|
|
67
67
|
} catch (err) {
|
|
68
68
|
console.error(`forgecss: error extracting styles.`, err);
|
|
69
69
|
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import postcss from "postcss";
|
|
2
|
-
import safeParser from "postcss-safe-parser";
|
|
3
|
-
|
|
4
|
-
let INVENTORY = {};
|
|
5
|
-
|
|
6
|
-
export async function extractStyles(css = null) {
|
|
7
|
-
const content = css;
|
|
8
|
-
INVENTORY['styles.css'] = postcss.parse(content, { parser: safeParser });
|
|
9
|
-
}
|
|
10
|
-
export function getStylesByClassName(selector) {
|
|
11
|
-
const decls = [];
|
|
12
|
-
Object.keys(INVENTORY).forEach((filePath) => {
|
|
13
|
-
INVENTORY[filePath].walkRules((rule) => {
|
|
14
|
-
if (rule.selectors && rule.selectors.includes(`.${selector}`)) {
|
|
15
|
-
rule.walkDecls((d) => {
|
|
16
|
-
decls.push({ prop: d.prop, value: d.value, important: d.important });
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
if (decls.length === 0) {
|
|
22
|
-
console.warn(`forgecss: no styles found for class "${selector}".`);
|
|
23
|
-
}
|
|
24
|
-
return decls;
|
|
25
|
-
}
|
|
26
|
-
export function invalidateInventory() {
|
|
27
|
-
INVENTORY = {};
|
|
28
|
-
}
|
|
29
|
-
export function resolveApplys() {
|
|
30
|
-
let resolvedApplies;
|
|
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 (!resolvedApplies) {
|
|
48
|
-
resolvedApplies = postcss.root();
|
|
49
|
-
}
|
|
50
|
-
resolvedApplies.append(newRule);
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
return resolvedApplies;
|
|
56
|
-
}
|
|
57
|
-
export function getInventory() {
|
|
58
|
-
return INVENTORY;
|
|
59
|
-
}
|
package/standalone/lib/usages.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { fromHtml } from "hast-util-from-html";
|
|
2
|
-
import { visit } from "unist-util-visit";
|
|
3
|
-
|
|
4
|
-
let USAGES = {};
|
|
5
|
-
|
|
6
|
-
export async function findUsages(filePath, fileContent = null) {
|
|
7
|
-
try {
|
|
8
|
-
if (USAGES[filePath]) {
|
|
9
|
-
// already processed
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
USAGES[filePath] = [];
|
|
13
|
-
const content = fileContent;
|
|
14
|
-
|
|
15
|
-
const ast = fromHtml(content);
|
|
16
|
-
visit(ast, "element", (node) => {
|
|
17
|
-
if (node.properties.className) {
|
|
18
|
-
USAGES[filePath].push(node.properties.className.join(" "));
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
} catch (err) {
|
|
22
|
-
console.error(`forgecss: error processing file ${filePath}`, err);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
export function invalidateUsageCache() {
|
|
26
|
-
USAGES = {};
|
|
27
|
-
}
|
|
28
|
-
export function getUsages() {
|
|
29
|
-
return USAGES;
|
|
30
|
-
}
|