catom 1.2.8 → 2.0.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/dist/css.d.ts +39 -5
- package/dist/css.d.ts.map +1 -0
- package/dist/css.js +85 -33
- package/dist/hash.d.ts +7 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +37 -0
- package/dist/index.d.ts +32 -7
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -10
- package/dist/plugin.d.ts +16 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +114 -0
- package/dist/transformer.d.ts +14 -0
- package/dist/transformer.d.ts.map +1 -0
- package/dist/transformer.js +87 -0
- package/package.json +35 -28
- package/LICENSE +0 -21
- package/README.md +0 -150
- package/babelPlugin.d.ts +0 -1
- package/babelPlugin.js +0 -1
- package/css.d.ts +0 -1
- package/css.js +0 -1
- package/dist/babelPlugin.d.ts +0 -4
- package/dist/babelPlugin.js +0 -59
- package/dist/plugin/astObject.d.ts +0 -3
- package/dist/plugin/astObject.js +0 -62
- package/dist/plugin/constants.d.ts +0 -17
- package/dist/plugin/constants.js +0 -9
- package/dist/plugin/cssTransform.d.ts +0 -3
- package/dist/plugin/cssTransform.js +0 -92
- package/dist/plugin/hash.d.ts +0 -1
- package/dist/plugin/hash.js +0 -50
- package/tsconfig.json +0 -14
package/dist/css.d.ts
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
export interface CSSRule {
|
|
2
|
+
className: string;
|
|
3
|
+
rule: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Generate a CSS rule from a property-value pair.
|
|
7
|
+
* This is a pure function - same input always gives same output.
|
|
8
|
+
*
|
|
9
|
+
* @param property - CSS property name (camelCase or kebab-case)
|
|
10
|
+
* @param value - CSS value
|
|
11
|
+
* @param context - Optional context like media query or pseudo selector
|
|
12
|
+
* @returns CSSRule with className and the full CSS rule string
|
|
13
|
+
*/
|
|
14
|
+
export declare function generateRule(property: string, value: string | number, context?: {
|
|
15
|
+
media?: string;
|
|
16
|
+
pseudo?: string;
|
|
17
|
+
}): CSSRule;
|
|
18
|
+
export interface StyleObject {
|
|
19
|
+
[key: string]: string | number | StyleObject | undefined;
|
|
20
|
+
media?: Record<string, Record<string, string | number>>;
|
|
21
|
+
pseudo?: Record<string, Record<string, string | number>>;
|
|
22
|
+
}
|
|
23
|
+
export interface ProcessResult {
|
|
24
|
+
classNames: string[];
|
|
25
|
+
rules: string[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Process a style object and return class names + CSS rules.
|
|
29
|
+
* Pure function - no side effects, no global state.
|
|
30
|
+
*/
|
|
31
|
+
export declare function processStyleObject(obj: StyleObject, context?: {
|
|
32
|
+
media?: string;
|
|
33
|
+
pseudo?: string;
|
|
34
|
+
}): ProcessResult;
|
|
35
|
+
/**
|
|
36
|
+
* Deduplicate and sort CSS rules for consistent output.
|
|
37
|
+
*/
|
|
38
|
+
export declare function dedupeRules(rules: string[]): string;
|
|
39
|
+
//# sourceMappingURL=css.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../src/css.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,OAAO,CA2BT;AAED,MAAM,WAAW,WAAW;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,WAAW,EAChB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,aAAa,CAiCf;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAEnD"}
|
package/dist/css.js
CHANGED
|
@@ -1,35 +1,87 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
Object.defineProperty(exports, "emitCSS", { enumerable: true, get: function () { return cssTransform_1.emitCSS; } });
|
|
7
|
-
async function transformCSS(css, ...plugins) {
|
|
8
|
-
css = css || (0, cssTransform_1.emitCSS)();
|
|
9
|
-
const { code } = (0, css_1.transform)({
|
|
10
|
-
code: Buffer.from(css),
|
|
11
|
-
drafts: { customMedia: true, nesting: true },
|
|
12
|
-
minify: true,
|
|
13
|
-
filename: "style.css",
|
|
14
|
-
sourceMap: false,
|
|
15
|
-
// browserslist hardcoded
|
|
16
|
-
// probably change or manually audit
|
|
17
|
-
targets: {
|
|
18
|
-
android: 6422528,
|
|
19
|
-
chrome: 6488064,
|
|
20
|
-
edge: 6488064,
|
|
21
|
-
firefox: 6356992,
|
|
22
|
-
ie: 720896,
|
|
23
|
-
ios_saf: 983552,
|
|
24
|
-
opera: 5439488,
|
|
25
|
-
safari: 983552,
|
|
26
|
-
samsung: 1048576,
|
|
27
|
-
},
|
|
28
|
-
});
|
|
29
|
-
return code;
|
|
1
|
+
import { murmur2 } from "./hash";
|
|
2
|
+
const KEBAB_CASE_RE = /([a-z0-9]|(?=[A-Z]))([A-Z])/g;
|
|
3
|
+
const NUMERIC_START = /^[0-9-]/;
|
|
4
|
+
function toKebabCase(prop) {
|
|
5
|
+
return prop.replace(KEBAB_CASE_RE, "$1-$2").toLowerCase();
|
|
30
6
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return
|
|
7
|
+
function sanitizeClassName(hash) {
|
|
8
|
+
// CSS class names can't start with a digit or hyphen
|
|
9
|
+
return NUMERIC_START.test(hash) ? `_${hash}` : hash;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Generate a CSS rule from a property-value pair.
|
|
13
|
+
* This is a pure function - same input always gives same output.
|
|
14
|
+
*
|
|
15
|
+
* @param property - CSS property name (camelCase or kebab-case)
|
|
16
|
+
* @param value - CSS value
|
|
17
|
+
* @param context - Optional context like media query or pseudo selector
|
|
18
|
+
* @returns CSSRule with className and the full CSS rule string
|
|
19
|
+
*/
|
|
20
|
+
export function generateRule(property, value, context) {
|
|
21
|
+
const cssProp = toKebabCase(property.trim());
|
|
22
|
+
const cssValue = String(value).trim();
|
|
23
|
+
// The identity includes context so same property in different contexts gets different class
|
|
24
|
+
const identity = [
|
|
25
|
+
context?.media || "",
|
|
26
|
+
context?.pseudo || "",
|
|
27
|
+
cssProp,
|
|
28
|
+
cssValue,
|
|
29
|
+
].join("|");
|
|
30
|
+
const className = sanitizeClassName(murmur2(identity));
|
|
31
|
+
const declaration = `${cssProp}:${cssValue}`;
|
|
32
|
+
let rule;
|
|
33
|
+
const selector = context?.pseudo
|
|
34
|
+
? `.${className}${context.pseudo}`
|
|
35
|
+
: `.${className}`;
|
|
36
|
+
if (context?.media) {
|
|
37
|
+
rule = `@media ${context.media}{${selector}{${declaration}}}`;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
rule = `${selector}{${declaration}}`;
|
|
41
|
+
}
|
|
42
|
+
return { className, rule };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Process a style object and return class names + CSS rules.
|
|
46
|
+
* Pure function - no side effects, no global state.
|
|
47
|
+
*/
|
|
48
|
+
export function processStyleObject(obj, context) {
|
|
49
|
+
const classNames = [];
|
|
50
|
+
const rules = [];
|
|
51
|
+
for (const key of Object.keys(obj)) {
|
|
52
|
+
const value = obj[key];
|
|
53
|
+
if (value === undefined)
|
|
54
|
+
continue;
|
|
55
|
+
if (key === "media" && typeof value === "object") {
|
|
56
|
+
// Handle media queries: media: { "(max-width: 768px)": { color: "red" } }
|
|
57
|
+
for (const query of Object.keys(value)) {
|
|
58
|
+
const mediaStyles = value[query];
|
|
59
|
+
const result = processStyleObject(mediaStyles, { media: query });
|
|
60
|
+
classNames.push(...result.classNames);
|
|
61
|
+
rules.push(...result.rules);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else if (key === "pseudo" && typeof value === "object") {
|
|
65
|
+
// Handle pseudo selectors: pseudo: { ":hover": { color: "blue" } }
|
|
66
|
+
for (const selector of Object.keys(value)) {
|
|
67
|
+
const pseudoStyles = value[selector];
|
|
68
|
+
const result = processStyleObject(pseudoStyles, { pseudo: selector });
|
|
69
|
+
classNames.push(...result.classNames);
|
|
70
|
+
rules.push(...result.rules);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
else if (typeof value === "string" || typeof value === "number") {
|
|
74
|
+
// Regular CSS property
|
|
75
|
+
const { className, rule } = generateRule(key, value, context);
|
|
76
|
+
classNames.push(className);
|
|
77
|
+
rules.push(rule);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { classNames, rules };
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Deduplicate and sort CSS rules for consistent output.
|
|
84
|
+
*/
|
|
85
|
+
export function dedupeRules(rules) {
|
|
86
|
+
return [...new Set(rules)].sort().join("\n");
|
|
34
87
|
}
|
|
35
|
-
exports.autoPrefixCSS = autoPrefixCSS;
|
package/dist/hash.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAqC3C"}
|
package/dist/hash.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Murmur2 hash - deterministic hash function.
|
|
3
|
+
* Same input always produces the same output.
|
|
4
|
+
* Adapted from emotion-js.
|
|
5
|
+
*/
|
|
6
|
+
export function murmur2(str) {
|
|
7
|
+
let h = 0;
|
|
8
|
+
let k;
|
|
9
|
+
let i = 0;
|
|
10
|
+
let len = str.length;
|
|
11
|
+
for (; len >= 4; ++i, len -= 4) {
|
|
12
|
+
k =
|
|
13
|
+
(str.charCodeAt(i) & 0xff) |
|
|
14
|
+
((str.charCodeAt(++i) & 0xff) << 8) |
|
|
15
|
+
((str.charCodeAt(++i) & 0xff) << 16) |
|
|
16
|
+
((str.charCodeAt(++i) & 0xff) << 24);
|
|
17
|
+
k = (k & 0xffff) * 0x5bd1e995 + (((k >>> 16) * 0xe995) << 16);
|
|
18
|
+
k ^= k >>> 24;
|
|
19
|
+
h =
|
|
20
|
+
((k & 0xffff) * 0x5bd1e995 + (((k >>> 16) * 0xe995) << 16)) ^
|
|
21
|
+
((h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0xe995) << 16));
|
|
22
|
+
}
|
|
23
|
+
switch (len) {
|
|
24
|
+
case 3:
|
|
25
|
+
h ^= (str.charCodeAt(i + 2) & 0xff) << 16;
|
|
26
|
+
// fallthrough
|
|
27
|
+
case 2:
|
|
28
|
+
h ^= (str.charCodeAt(i + 1) & 0xff) << 8;
|
|
29
|
+
// fallthrough
|
|
30
|
+
case 1:
|
|
31
|
+
h ^= str.charCodeAt(i) & 0xff;
|
|
32
|
+
h = (h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0xe995) << 16);
|
|
33
|
+
}
|
|
34
|
+
h ^= h >>> 13;
|
|
35
|
+
h = (h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0xe995) << 16);
|
|
36
|
+
return ((h ^ (h >>> 15)) >>> 0).toString(36);
|
|
37
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,38 @@
|
|
|
1
|
-
import { Properties } from "csstype";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
};
|
|
5
|
-
|
|
1
|
+
import type { Properties } from "csstype";
|
|
2
|
+
export { default } from "./plugin";
|
|
3
|
+
export type { CatomOptions } from "./plugin";
|
|
4
|
+
export { processStyleObject, generateRule, dedupeRules } from "./css";
|
|
5
|
+
export type { CSSRule, StyleObject, ProcessResult } from "./css";
|
|
6
|
+
/**
|
|
7
|
+
* Define atomic CSS styles. Transformed at build time to class names.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { css } from "catom-vite";
|
|
12
|
+
*
|
|
13
|
+
* const className = css({
|
|
14
|
+
* display: "flex",
|
|
15
|
+
* alignItems: "center",
|
|
16
|
+
* padding: "1rem",
|
|
17
|
+
* media: {
|
|
18
|
+
* "(max-width: 768px)": {
|
|
19
|
+
* flexDirection: "column"
|
|
20
|
+
* }
|
|
21
|
+
* },
|
|
22
|
+
* pseudo: {
|
|
23
|
+
* ":hover": {
|
|
24
|
+
* backgroundColor: "#f0f0f0"
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare function css(_styles: Properties & {
|
|
6
31
|
media?: {
|
|
7
32
|
[query: string]: Properties;
|
|
8
33
|
};
|
|
9
34
|
pseudo?: {
|
|
10
|
-
[
|
|
35
|
+
[selector: string]: Properties;
|
|
11
36
|
};
|
|
12
37
|
}): string;
|
|
13
|
-
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAG7C,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACtE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEjE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,GAAG,CACjB,OAAO,EAAE,UAAU,GAAG;IACpB,KAAK,CAAC,EAAE;QAAE,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAC;IACxC,MAAM,CAAC,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAC;CAC7C,GACA,MAAM,CAKR"}
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
export { default } from "./plugin";
|
|
2
|
+
// Re-export utilities for advanced usage
|
|
3
|
+
export { processStyleObject, generateRule, dedupeRules } from "./css";
|
|
4
|
+
/**
|
|
5
|
+
* Define atomic CSS styles. Transformed at build time to class names.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { css } from "catom-vite";
|
|
10
|
+
*
|
|
11
|
+
* const className = css({
|
|
12
|
+
* display: "flex",
|
|
13
|
+
* alignItems: "center",
|
|
14
|
+
* padding: "1rem",
|
|
15
|
+
* media: {
|
|
16
|
+
* "(max-width: 768px)": {
|
|
17
|
+
* flexDirection: "column"
|
|
18
|
+
* }
|
|
19
|
+
* },
|
|
20
|
+
* pseudo: {
|
|
21
|
+
* ":hover": {
|
|
22
|
+
* backgroundColor: "#f0f0f0"
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function css(_styles) {
|
|
29
|
+
throw new Error("catom css() was called at runtime. " +
|
|
30
|
+
"Ensure the catom-vite plugin is configured in vite.config.ts");
|
|
10
31
|
}
|
|
11
|
-
exports.css = css;
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
import { transform } from "lightningcss";
|
|
3
|
+
export interface CatomOptions {
|
|
4
|
+
/** Function name to transform. Default: "css" */
|
|
5
|
+
functionName?: string;
|
|
6
|
+
/** LightningCSS browser targets */
|
|
7
|
+
targets?: Parameters<typeof transform>[0]["targets"];
|
|
8
|
+
/** Minify CSS output. Default: true in production */
|
|
9
|
+
minify?: boolean;
|
|
10
|
+
/** File patterns to include. Default: /\.[jt]sx?$/ */
|
|
11
|
+
include?: RegExp | RegExp[];
|
|
12
|
+
/** File patterns to exclude */
|
|
13
|
+
exclude?: RegExp | RegExp[];
|
|
14
|
+
}
|
|
15
|
+
export default function catom(options?: CatomOptions): Plugin[];
|
|
16
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAKzC,MAAM,WAAW,YAAY;IAC3B,iDAAiD;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACrD,qDAAqD;IACrD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC7B;AAWD,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,MAAM,EAAE,CAwHlE"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { transform } from "lightningcss";
|
|
2
|
+
import AST from "unplugin-ast/vite";
|
|
3
|
+
import { createCatomTransformer } from "./transformer";
|
|
4
|
+
import { dedupeRules } from "./css";
|
|
5
|
+
const VIRTUAL_ID = "virtual:catom.css";
|
|
6
|
+
const RESOLVED_VIRTUAL_ID = "\0" + VIRTUAL_ID;
|
|
7
|
+
const DEFAULT_TARGETS = {
|
|
8
|
+
chrome: 80 << 16,
|
|
9
|
+
firefox: 80 << 16,
|
|
10
|
+
safari: 14 << 16,
|
|
11
|
+
};
|
|
12
|
+
export default function catom(options = {}) {
|
|
13
|
+
const { functionName = "css", targets = DEFAULT_TARGETS, minify, include = /\.[jt]sx?$/, exclude, } = options;
|
|
14
|
+
// Collect CSS rules from all files.
|
|
15
|
+
// Using a Set for automatic deduplication (deterministic hashing means same rule = same string)
|
|
16
|
+
const collectedRules = new Set();
|
|
17
|
+
let isDev = false;
|
|
18
|
+
// The AST plugin handles transformation
|
|
19
|
+
const astPlugin = AST({
|
|
20
|
+
include: Array.isArray(include) ? include : [include],
|
|
21
|
+
exclude: exclude ? (Array.isArray(exclude) ? exclude : [exclude]) : undefined,
|
|
22
|
+
enforce: "pre",
|
|
23
|
+
transformer: [
|
|
24
|
+
createCatomTransformer(functionName, (rules) => {
|
|
25
|
+
for (const rule of rules) {
|
|
26
|
+
collectedRules.add(rule);
|
|
27
|
+
}
|
|
28
|
+
}),
|
|
29
|
+
],
|
|
30
|
+
});
|
|
31
|
+
// The CSS plugin handles virtual module and CSS emission
|
|
32
|
+
const cssPlugin = {
|
|
33
|
+
name: "catom:css",
|
|
34
|
+
enforce: "post",
|
|
35
|
+
configResolved(config) {
|
|
36
|
+
isDev = config.command === "serve";
|
|
37
|
+
},
|
|
38
|
+
buildStart() {
|
|
39
|
+
// Clear collected rules at the start of each build
|
|
40
|
+
collectedRules.clear();
|
|
41
|
+
},
|
|
42
|
+
resolveId(id) {
|
|
43
|
+
if (id === VIRTUAL_ID) {
|
|
44
|
+
return RESOLVED_VIRTUAL_ID;
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
load(id) {
|
|
48
|
+
if (id === RESOLVED_VIRTUAL_ID) {
|
|
49
|
+
// In dev, return empty - we inject via transformIndexHtml
|
|
50
|
+
// In build, this will be populated by generateBundle
|
|
51
|
+
return "";
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
// For dev: inject CSS into HTML
|
|
55
|
+
transformIndexHtml: {
|
|
56
|
+
order: "post",
|
|
57
|
+
handler() {
|
|
58
|
+
if (collectedRules.size === 0)
|
|
59
|
+
return [];
|
|
60
|
+
const css = dedupeRules([...collectedRules]);
|
|
61
|
+
const shouldMinify = minify ?? !isDev;
|
|
62
|
+
let finalCss;
|
|
63
|
+
try {
|
|
64
|
+
const { code } = transform({
|
|
65
|
+
code: Buffer.from(css),
|
|
66
|
+
filename: "catom.css",
|
|
67
|
+
minify: shouldMinify,
|
|
68
|
+
targets,
|
|
69
|
+
});
|
|
70
|
+
finalCss = code.toString();
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Fallback if lightningcss fails
|
|
74
|
+
finalCss = css;
|
|
75
|
+
}
|
|
76
|
+
return [
|
|
77
|
+
{
|
|
78
|
+
tag: "style",
|
|
79
|
+
attrs: { "data-catom": "" },
|
|
80
|
+
children: finalCss,
|
|
81
|
+
injectTo: "head",
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
// For build: emit CSS file
|
|
87
|
+
generateBundle() {
|
|
88
|
+
if (collectedRules.size === 0)
|
|
89
|
+
return;
|
|
90
|
+
const css = dedupeRules([...collectedRules]);
|
|
91
|
+
const shouldMinify = minify ?? true;
|
|
92
|
+
let finalCss;
|
|
93
|
+
try {
|
|
94
|
+
const { code } = transform({
|
|
95
|
+
code: Buffer.from(css),
|
|
96
|
+
filename: "catom.css",
|
|
97
|
+
minify: shouldMinify,
|
|
98
|
+
targets,
|
|
99
|
+
});
|
|
100
|
+
finalCss = code.toString();
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
finalCss = css;
|
|
104
|
+
}
|
|
105
|
+
this.emitFile({
|
|
106
|
+
type: "asset",
|
|
107
|
+
fileName: "catom.css",
|
|
108
|
+
source: finalCss,
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
// Return both plugins - AST first (pre), then CSS (post)
|
|
113
|
+
return [astPlugin, cssPlugin];
|
|
114
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CallExpression } from "@babel/types";
|
|
2
|
+
import type { Transformer } from "unplugin-ast";
|
|
3
|
+
export interface TransformResult {
|
|
4
|
+
classNames: string;
|
|
5
|
+
rules: string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Creates an unplugin-ast transformer that finds css() calls
|
|
9
|
+
* and replaces them with class name strings.
|
|
10
|
+
*
|
|
11
|
+
* Returns the collected CSS rules separately for aggregation.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createCatomTransformer(functionName: string, onRules: (rules: string[]) => void): Transformer<CallExpression>;
|
|
14
|
+
//# sourceMappingURL=transformer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformer.d.ts","sourceRoot":"","sources":["../src/transformer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAA0B,MAAM,cAAc,CAAC;AAC3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAoDhD,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,GACjC,WAAW,CAAC,cAAc,CAAC,CAkC7B"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { processStyleObject } from "./css";
|
|
2
|
+
/**
|
|
3
|
+
* Extract a JS value from a Babel AST node.
|
|
4
|
+
* Only handles static, compile-time constant values.
|
|
5
|
+
*/
|
|
6
|
+
function extractValue(node) {
|
|
7
|
+
switch (node.type) {
|
|
8
|
+
case "StringLiteral":
|
|
9
|
+
return node.value;
|
|
10
|
+
case "NumericLiteral":
|
|
11
|
+
return node.value;
|
|
12
|
+
case "ObjectExpression":
|
|
13
|
+
return extractObject(node);
|
|
14
|
+
case "TSAsExpression":
|
|
15
|
+
return extractValue(node.expression);
|
|
16
|
+
case "Identifier":
|
|
17
|
+
if (node.name === "undefined")
|
|
18
|
+
return undefined;
|
|
19
|
+
throw new Error(`Cannot evaluate identifier "${node.name}" at compile time`);
|
|
20
|
+
default:
|
|
21
|
+
throw new Error(`Cannot evaluate ${node.type} at compile time`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function extractObject(node) {
|
|
25
|
+
const result = {};
|
|
26
|
+
for (const prop of node.properties) {
|
|
27
|
+
if (prop.type === "SpreadElement") {
|
|
28
|
+
// Handle spread of object literals
|
|
29
|
+
if (prop.argument.type === "ObjectExpression") {
|
|
30
|
+
Object.assign(result, extractObject(prop.argument));
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
throw new Error("Spread elements must be object literals at compile time");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else if (prop.type === "ObjectProperty") {
|
|
37
|
+
let key;
|
|
38
|
+
if (prop.key.type === "Identifier") {
|
|
39
|
+
key = prop.key.name;
|
|
40
|
+
}
|
|
41
|
+
else if (prop.key.type === "StringLiteral") {
|
|
42
|
+
key = prop.key.value;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
throw new Error(`Unsupported key type: ${prop.key.type}`);
|
|
46
|
+
}
|
|
47
|
+
result[key] = extractValue(prop.value);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Creates an unplugin-ast transformer that finds css() calls
|
|
54
|
+
* and replaces them with class name strings.
|
|
55
|
+
*
|
|
56
|
+
* Returns the collected CSS rules separately for aggregation.
|
|
57
|
+
*/
|
|
58
|
+
export function createCatomTransformer(functionName, onRules) {
|
|
59
|
+
return {
|
|
60
|
+
onNode(node) {
|
|
61
|
+
return (node.type === "CallExpression" &&
|
|
62
|
+
node.callee.type === "Identifier" &&
|
|
63
|
+
node.callee.name === functionName);
|
|
64
|
+
},
|
|
65
|
+
transform(node) {
|
|
66
|
+
const arg = node.arguments[0];
|
|
67
|
+
if (!arg || arg.type !== "ObjectExpression") {
|
|
68
|
+
throw new Error(`${functionName}() requires an object literal argument`);
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const styleObj = extractObject(arg);
|
|
72
|
+
const { classNames, rules } = processStyleObject(styleObj);
|
|
73
|
+
// Report rules to the plugin
|
|
74
|
+
onRules(rules);
|
|
75
|
+
// Return a string literal with the class names
|
|
76
|
+
return {
|
|
77
|
+
type: "StringLiteral",
|
|
78
|
+
value: classNames.join(" "),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
83
|
+
throw new Error(`Failed to process ${functionName}(): ${message}`);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
package/package.json
CHANGED
|
@@ -1,39 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "catom",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Zero-runtime atomic CSS-in-JS for Vite",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"prepublishOnly": "npm run build",
|
|
8
|
-
"prebuild": "rm -rf ./dist",
|
|
9
|
-
"build": "tsc --build tsconfig.json"
|
|
10
|
-
},
|
|
11
|
-
"repository": {
|
|
12
|
-
"type": "git",
|
|
13
|
-
"url": "git+https://github.com/Hydrophobefireman/catom.git"
|
|
14
|
-
},
|
|
15
|
-
"license": "MIT",
|
|
16
|
-
"bugs": {
|
|
17
|
-
"url": "https://github.com/Hydrophobefireman/catom/issues"
|
|
18
|
-
},
|
|
19
7
|
"module": "dist/index.js",
|
|
20
8
|
"types": "dist/index.d.ts",
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"@types/babel__traverse": "^7.14.2",
|
|
27
|
-
"@types/estree": "^0.0.50",
|
|
28
|
-
"@types/node": "^16.10.8",
|
|
29
|
-
"typescript": "^4.4.4"
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
30
14
|
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc",
|
|
20
|
+
"dev": "tsc --watch",
|
|
21
|
+
"prepublishOnly": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"vite",
|
|
25
|
+
"vite-plugin",
|
|
26
|
+
"css-in-js",
|
|
27
|
+
"atomic-css",
|
|
28
|
+
"zero-runtime"
|
|
29
|
+
],
|
|
30
|
+
"license": "MIT",
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"
|
|
32
|
+
"vite": "^4.0.0 || ^5.0.0 || ^6.0.0",
|
|
33
|
+
"lightningcss": "^1.16.0"
|
|
33
34
|
},
|
|
34
35
|
"dependencies": {
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
|
|
36
|
+
"unplugin-ast": "^0.15.4",
|
|
37
|
+
"csstype": "^3.1.1"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@babel/types": "^7.26.0",
|
|
41
|
+
"@types/node": "^20.0.0",
|
|
42
|
+
"typescript": "^5.0.0",
|
|
43
|
+
"vite": "^5.0.0",
|
|
44
|
+
"lightningcss": "^1.28.0"
|
|
38
45
|
}
|
|
39
46
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2020 Hydrophobefireman
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/README.md
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
# Catom
|
|
2
|
-
|
|
3
|
-
## A 0 runtime css in ~~js~~ css tool
|
|
4
|
-
|
|
5
|
-
### Stage: Pre Alpha ([Caveats](#caveats))
|
|
6
|
-
|
|
7
|
-
Catom allows you to write CSS in your javascript/typescript file and creates highly optimized CSS out of it.
|
|
8
|
-
|
|
9
|
-
Each rule creates a unique class definition out of it and it gives you 100% freedom about where to put your generated css bundle.
|
|
10
|
-
|
|
11
|
-
Your javascript code has 0 references to any styles and all that's left is are the compiled hashed clasnames as a string.
|
|
12
|
-
|
|
13
|
-
It's framework agnostic as it emits pure CSS and leaves out just the classnames
|
|
14
|
-
|
|
15
|
-
## Example
|
|
16
|
-
|
|
17
|
-
somewhere in our App.js
|
|
18
|
-
|
|
19
|
-
```javascript
|
|
20
|
-
import { css } from "catom";
|
|
21
|
-
|
|
22
|
-
const styledButton = css({
|
|
23
|
-
color: "#ff0000",
|
|
24
|
-
borderRadius: "5px",
|
|
25
|
-
padding: "4px",
|
|
26
|
-
});
|
|
27
|
-
const styledDiv = css({ color: "blue", borderRadius: "5px", padding: "4px" });
|
|
28
|
-
|
|
29
|
-
function App() {
|
|
30
|
-
return (
|
|
31
|
-
<div className={styledDiv}>
|
|
32
|
-
<button className={styledButton}>Hi</button>
|
|
33
|
-
</div>
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
**Css generated:**
|
|
39
|
-
|
|
40
|
-
```css
|
|
41
|
-
._6da32 {
|
|
42
|
-
color: #ff0000;
|
|
43
|
-
}
|
|
44
|
-
.quva1q {
|
|
45
|
-
border-radius: 5px;
|
|
46
|
-
}
|
|
47
|
-
._2rlxtj {
|
|
48
|
-
padding: 4px;
|
|
49
|
-
}
|
|
50
|
-
._14ksm7b {
|
|
51
|
-
color: blue;
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
**App.js:**
|
|
56
|
-
|
|
57
|
-
```js
|
|
58
|
-
const styledButton = "_6da32 quva1q _2rlxtj";
|
|
59
|
-
const styledDiv = "_14ksm7b quva1q _2rlxtj";
|
|
60
|
-
....
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
As we had only 4 unique rules, catom generated only 4 classes.
|
|
64
|
-
|
|
65
|
-
Catom also supports media queries and pseudo properties passing them in an object
|
|
66
|
-
|
|
67
|
-
```javascript
|
|
68
|
-
const mediaQuery = css({
|
|
69
|
-
media: { "only screen and (max-width:500px)": { color: "red" } },
|
|
70
|
-
});
|
|
71
|
-
const pseudoQuery = css({ pseudo: { ":hover": { color: "green" } } });
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
# Installation and Usage
|
|
75
|
-
|
|
76
|
-
Install using npm or yarn
|
|
77
|
-
|
|
78
|
-
```
|
|
79
|
-
npm i catom -D
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
In your babel config:
|
|
83
|
-
|
|
84
|
-
```json
|
|
85
|
-
{
|
|
86
|
-
"plugins": [
|
|
87
|
-
"catom/babelPlugin"
|
|
88
|
-
`....
|
|
89
|
-
]
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
As catom doesn't really interact with your build tool at all, it's your job to inject the generated style.
|
|
95
|
-
|
|
96
|
-
Here's an example of how you can use it with HTMLWebpackPlugin.
|
|
97
|
-
|
|
98
|
-
`webpack.confg.js`
|
|
99
|
-
|
|
100
|
-
```js
|
|
101
|
-
const { emitCSS } = require("catom/css");
|
|
102
|
-
// ...
|
|
103
|
-
module.exports = {
|
|
104
|
-
plugins: [
|
|
105
|
-
new HtmlWebpackPlugin({
|
|
106
|
-
templateParameters: async function templateParametersGenerator(
|
|
107
|
-
compilation,
|
|
108
|
-
files,
|
|
109
|
-
tags,
|
|
110
|
-
options
|
|
111
|
-
) {
|
|
112
|
-
return {
|
|
113
|
-
compilation,
|
|
114
|
-
webpackConfig: compilation.options,
|
|
115
|
-
htmlWebpackPlugin: {
|
|
116
|
-
tags,
|
|
117
|
-
files,
|
|
118
|
-
options: Object.assign(options, {
|
|
119
|
-
emitCSS,
|
|
120
|
-
}),
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
},
|
|
124
|
-
}),
|
|
125
|
-
],
|
|
126
|
-
};
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
and then inject it using a template parameter.
|
|
130
|
-
|
|
131
|
-
```html
|
|
132
|
-
<head>
|
|
133
|
-
<style>
|
|
134
|
-
<%= htmlWebpackPlugin.options.emitCSS() %>
|
|
135
|
-
</style>
|
|
136
|
-
</head>
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
uses parcel css under the hood
|
|
140
|
-
|
|
141
|
-
# 0 Runtime
|
|
142
|
-
|
|
143
|
-
Catom ships with 0 js code in your bundle. In fact the first thing the babel transform does, is to [remove](https://github.com/Hydrophobefireman/catom/blob/378fefef245c399a550edb60916c051f87f671ea/babelPlugin.ts#L17) all imports of the `css` function from your code.
|
|
144
|
-
|
|
145
|
-
# Caveats
|
|
146
|
-
|
|
147
|
-
- It's just something I threw together because I wanted it for a project
|
|
148
|
-
- Not even close to production ready
|
|
149
|
-
- Since it works with AST, it does not allow you to use variable in the values (In work)
|
|
150
|
-
- No support for keyframes as of now
|
package/babelPlugin.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./dist/babelPlugin";
|
package/babelPlugin.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
module.exports = require("./dist/babelPlugin");
|
package/css.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./dist/css";
|
package/css.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
module.exports = require("./dist/css");
|
package/dist/babelPlugin.d.ts
DELETED
package/dist/babelPlugin.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
-
}) : (function(o, m, k, k2) {
|
|
6
|
-
if (k2 === undefined) k2 = k;
|
|
7
|
-
o[k2] = m[k];
|
|
8
|
-
}));
|
|
9
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
-
}) : function(o, v) {
|
|
12
|
-
o["default"] = v;
|
|
13
|
-
});
|
|
14
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
-
if (mod && mod.__esModule) return mod;
|
|
16
|
-
var result = {};
|
|
17
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
-
__setModuleDefault(result, mod);
|
|
19
|
-
return result;
|
|
20
|
-
};
|
|
21
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
const template = __importStar(require("@babel/template"));
|
|
23
|
-
const astObject_1 = require("./plugin/astObject");
|
|
24
|
-
function handleExpression(path, expression, removeName) {
|
|
25
|
-
if (!expression)
|
|
26
|
-
return;
|
|
27
|
-
if (expression.type === "CallExpression") {
|
|
28
|
-
return commonInject(path, expression, {
|
|
29
|
-
name: removeName,
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
function catomBabelPlugin() {
|
|
34
|
-
let removeName = "css";
|
|
35
|
-
return {
|
|
36
|
-
visitor: {
|
|
37
|
-
CallExpression(path) {
|
|
38
|
-
return handleExpression(path, path.node, removeName);
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
exports.default = catomBabelPlugin;
|
|
44
|
-
function commonInject(path, right, options) {
|
|
45
|
-
const callee = right.callee;
|
|
46
|
-
if (callee && callee.type === "Identifier") {
|
|
47
|
-
if (callee.name === options.name) {
|
|
48
|
-
const arg0 = right.arguments[0];
|
|
49
|
-
return injectDependency(path, arg0);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
function injectDependency(path, arg0) {
|
|
54
|
-
if (arg0.type === "ObjectExpression") {
|
|
55
|
-
let retArray = [];
|
|
56
|
-
(0, astObject_1.parseObjectExpression)(arg0, retArray);
|
|
57
|
-
path.replaceWith(template.statement.ast(JSON.stringify(retArray.join(" "))));
|
|
58
|
-
}
|
|
59
|
-
}
|
package/dist/plugin/astObject.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseObjectExpression = void 0;
|
|
4
|
-
const cssTransform_1 = require("./cssTransform");
|
|
5
|
-
function parseObjectExpression(object, retArray, hashable) {
|
|
6
|
-
return object.properties.forEach((p) => handleProperties(p, retArray, hashable));
|
|
7
|
-
}
|
|
8
|
-
exports.parseObjectExpression = parseObjectExpression;
|
|
9
|
-
function handleSpread(spread, retArray, hashable) {
|
|
10
|
-
const argument = spread.argument;
|
|
11
|
-
if (argument.type === "ObjectExpression") {
|
|
12
|
-
return parseObjectExpression(argument, retArray, hashable);
|
|
13
|
-
}
|
|
14
|
-
throw Error(`Cannot parse ${spread.type}. Catom compiler only accepts compile time constant values`);
|
|
15
|
-
}
|
|
16
|
-
function handleProperties(propertyOrSpread, retArray, hashable) {
|
|
17
|
-
if (propertyOrSpread.type === "SpreadElement") {
|
|
18
|
-
return handleSpread(propertyOrSpread, retArray, hashable);
|
|
19
|
-
}
|
|
20
|
-
let { key, value } = propertyOrSpread;
|
|
21
|
-
if (value.type === "TSAsExpression")
|
|
22
|
-
value = value.expression;
|
|
23
|
-
let keyName;
|
|
24
|
-
if (key.type === "StringLiteral") {
|
|
25
|
-
keyName = key.value;
|
|
26
|
-
}
|
|
27
|
-
else if (key.type === "Identifier") {
|
|
28
|
-
keyName = key.name;
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
return throwErr();
|
|
32
|
-
}
|
|
33
|
-
const isMedia = keyName === "media";
|
|
34
|
-
const isPseudo = keyName === "pseudo";
|
|
35
|
-
const canAcceptObjectLiteralInValue = isMedia || isPseudo;
|
|
36
|
-
if (value.type === "StringLiteral" || value.type === "NumericLiteral") {
|
|
37
|
-
if (canAcceptObjectLiteralInValue)
|
|
38
|
-
throwErr("Need an object literal for media query or pseudo selector");
|
|
39
|
-
return retArray.push((0, cssTransform_1.createValueHash)(keyName, value.value, hashable));
|
|
40
|
-
}
|
|
41
|
-
if (canAcceptObjectLiteralInValue && value.type === "ObjectExpression") {
|
|
42
|
-
return value.properties.forEach((prop) => handleMediaOrPseudoProperties(prop, retArray, isMedia, isPseudo));
|
|
43
|
-
}
|
|
44
|
-
throwErr();
|
|
45
|
-
}
|
|
46
|
-
function handleMediaOrPseudoProperties(property, retArray, isMedia, isPseudo) {
|
|
47
|
-
if (property.type === "ObjectProperty") {
|
|
48
|
-
const { key, value } = property;
|
|
49
|
-
let keyName;
|
|
50
|
-
if (key.type === "StringLiteral")
|
|
51
|
-
keyName = key.value;
|
|
52
|
-
else if (key.type === "Identifier")
|
|
53
|
-
keyName = key.name;
|
|
54
|
-
if (value.type === "ObjectExpression") {
|
|
55
|
-
return parseObjectExpression(value, retArray, (isMedia && { media: keyName }) || (isPseudo && { pseudo: keyName }));
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
throwErr();
|
|
59
|
-
}
|
|
60
|
-
function throwErr(err) {
|
|
61
|
-
throw TypeError(err || "Catom only accepts literals and compile time constant values");
|
|
62
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export { Program } from "estree";
|
|
2
|
-
export declare type PropMap = Map<string, CSSProps>;
|
|
3
|
-
export interface AtomicCSSOptions {
|
|
4
|
-
transpileFunctionName: "css";
|
|
5
|
-
}
|
|
6
|
-
export interface CSSProps {
|
|
7
|
-
class: string;
|
|
8
|
-
cssRule: string;
|
|
9
|
-
}
|
|
10
|
-
export interface Hashable {
|
|
11
|
-
media?: string;
|
|
12
|
-
pseudo?: string;
|
|
13
|
-
}
|
|
14
|
-
export declare const NAME = "AtomicCssWebpackPlugin";
|
|
15
|
-
export declare const defaultOptions: AtomicCSSOptions;
|
|
16
|
-
export declare const KEBAB_CASE_REGEXP: RegExp;
|
|
17
|
-
export declare const PREFIX_WITH_UNDERSCORE: string[];
|
package/dist/plugin/constants.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PREFIX_WITH_UNDERSCORE = exports.KEBAB_CASE_REGEXP = exports.defaultOptions = exports.NAME = void 0;
|
|
4
|
-
exports.NAME = "AtomicCssWebpackPlugin";
|
|
5
|
-
exports.defaultOptions = {
|
|
6
|
-
transpileFunctionName: "css",
|
|
7
|
-
};
|
|
8
|
-
exports.KEBAB_CASE_REGEXP = /([a-z0-9]|(?=[A-Z]))([A-Z])/g;
|
|
9
|
-
exports.PREFIX_WITH_UNDERSCORE = "1234567890-".split("");
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.emitCSS = exports.createValueHash = void 0;
|
|
4
|
-
const constants_1 = require("./constants");
|
|
5
|
-
const hash_1 = require("./hash");
|
|
6
|
-
const CSS_PROPERTY_MAP = new Map();
|
|
7
|
-
const MEDIA_QUERY_MAP = new Map();
|
|
8
|
-
const PSEUDO_SELECTOR_MAP = new Map();
|
|
9
|
-
// function clearMaps(): void {
|
|
10
|
-
// CSS_PROPERTY_MAP.clear();
|
|
11
|
-
// MEDIA_QUERY_MAP.clear();
|
|
12
|
-
// PSEUDO_SELECTOR_MAP.clear();
|
|
13
|
-
// }
|
|
14
|
-
function toCSSProp(prop) {
|
|
15
|
-
return prop.replace(constants_1.KEBAB_CASE_REGEXP, "$1-$2").toLowerCase();
|
|
16
|
-
}
|
|
17
|
-
function makeCSSCompat(str) {
|
|
18
|
-
if (constants_1.PREFIX_WITH_UNDERSCORE.indexOf(str[0]) > -1)
|
|
19
|
-
return `_${str}`;
|
|
20
|
-
return str;
|
|
21
|
-
}
|
|
22
|
-
const sanitizeRegExp = /([^\w]|_)/g;
|
|
23
|
-
function createValueHash(key, unparsed, hashable) {
|
|
24
|
-
key = key.trim();
|
|
25
|
-
const value = String(unparsed).trim();
|
|
26
|
-
const MEDIA_QUERY = hashable && hashable.media;
|
|
27
|
-
const PSEUDO_SELECTOR = hashable && hashable.pseudo;
|
|
28
|
-
const isSpec = MEDIA_QUERY || PSEUDO_SELECTOR;
|
|
29
|
-
// example: const rawCSSRule = "margin:auto;"
|
|
30
|
-
const rawCSSRule = `${toCSSProp(key)}:${value};`;
|
|
31
|
-
// a unique rule will be one with a different media/pseudo rule + key&value
|
|
32
|
-
const identity = ((hashable && (MEDIA_QUERY || PSEUDO_SELECTOR)) || "").trim() + rawCSSRule;
|
|
33
|
-
let cache;
|
|
34
|
-
const $map = isSpec && MEDIA_QUERY ? MEDIA_QUERY_MAP : PSEUDO_SELECTOR_MAP;
|
|
35
|
-
let fetchMap;
|
|
36
|
-
if ($map) {
|
|
37
|
-
fetchMap = $map.get(isSpec);
|
|
38
|
-
if (!fetchMap) {
|
|
39
|
-
fetchMap = new Map();
|
|
40
|
-
$map.set(isSpec, fetchMap);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
if (isSpec) {
|
|
44
|
-
cache = fetchMap.get(identity);
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
cache = CSS_PROPERTY_MAP.get(identity);
|
|
48
|
-
}
|
|
49
|
-
if (cache)
|
|
50
|
-
return cache.class;
|
|
51
|
-
const hash = makeCSSCompat((0, hash_1.murmur2)(identity));
|
|
52
|
-
const obj = { class: hash, cssRule: rawCSSRule };
|
|
53
|
-
if (isSpec) {
|
|
54
|
-
fetchMap.set(identity, obj);
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
CSS_PROPERTY_MAP.set(identity, obj);
|
|
58
|
-
}
|
|
59
|
-
return hash;
|
|
60
|
-
}
|
|
61
|
-
exports.createValueHash = createValueHash;
|
|
62
|
-
const toCSS = (m) => Array.from(m.values())
|
|
63
|
-
.map((v) => `.${v.class} { ${v.cssRule} }`)
|
|
64
|
-
.sort()
|
|
65
|
-
.join("\n");
|
|
66
|
-
function mergeDuplicateRules(value, dedupedPropMap, suffix = "") {
|
|
67
|
-
const cls = `.${value.class}${suffix}`;
|
|
68
|
-
const rule = value.cssRule;
|
|
69
|
-
let arr = dedupedPropMap.get(rule);
|
|
70
|
-
if (!arr) {
|
|
71
|
-
arr = new Set();
|
|
72
|
-
dedupedPropMap.set(rule, arr);
|
|
73
|
-
}
|
|
74
|
-
arr.add(cls);
|
|
75
|
-
}
|
|
76
|
-
function emitCSS() {
|
|
77
|
-
const dedupedPropMap = new Map();
|
|
78
|
-
Array.from(CSS_PROPERTY_MAP.values()).forEach((v) => mergeDuplicateRules(v, dedupedPropMap));
|
|
79
|
-
Array.from(PSEUDO_SELECTOR_MAP.entries()).forEach(([k, v]) => Array.from(v.values())
|
|
80
|
-
.sort()
|
|
81
|
-
.forEach((prop) => mergeDuplicateRules(prop, dedupedPropMap, k)));
|
|
82
|
-
const mediaQueries = Array.from(MEDIA_QUERY_MAP.entries())
|
|
83
|
-
.map(([k, v]) => `@media ${k} {\n${toCSS(v)}\n}\n`)
|
|
84
|
-
.sort()
|
|
85
|
-
.join("\n");
|
|
86
|
-
const cssProps = Array.from(dedupedPropMap.entries())
|
|
87
|
-
.map(([rule, classNames]) => `${Array.from(classNames).join(",\n")}{ ${rule} }`)
|
|
88
|
-
.sort()
|
|
89
|
-
.join("\n");
|
|
90
|
-
return [cssProps, mediaQueries].join("\n");
|
|
91
|
-
}
|
|
92
|
-
exports.emitCSS = emitCSS;
|
package/dist/plugin/hash.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function murmur2(str: string): string;
|
package/dist/plugin/hash.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// from https://github.com/emotion-js/emotion/blob/master/packages/hash/src/index.js
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.murmur2 = void 0;
|
|
5
|
-
function murmur2(str) {
|
|
6
|
-
// 'm' and 'r' are mixing constants generated offline.
|
|
7
|
-
// They're not really 'magic', they just happen to work well.
|
|
8
|
-
// const m = 0x5bd1e995;
|
|
9
|
-
// const r = 24;
|
|
10
|
-
// Initialize the hash
|
|
11
|
-
var h = 0;
|
|
12
|
-
// Mix 4 bytes at a time into the hash
|
|
13
|
-
var k, i = 0, len = str.length;
|
|
14
|
-
for (; len >= 4; ++i, len -= 4) {
|
|
15
|
-
k =
|
|
16
|
-
(str.charCodeAt(i) & 0xff) |
|
|
17
|
-
((str.charCodeAt(++i) & 0xff) << 8) |
|
|
18
|
-
((str.charCodeAt(++i) & 0xff) << 16) |
|
|
19
|
-
((str.charCodeAt(++i) & 0xff) << 24);
|
|
20
|
-
k =
|
|
21
|
-
/* Math.imul(k, m): */
|
|
22
|
-
(k & 0xffff) * 0x5bd1e995 + (((k >>> 16) * 0xe995) << 16);
|
|
23
|
-
k ^= /* k >>> r: */ k >>> 24;
|
|
24
|
-
h =
|
|
25
|
-
/* Math.imul(k, m): */
|
|
26
|
-
((k & 0xffff) * 0x5bd1e995 + (((k >>> 16) * 0xe995) << 16)) ^
|
|
27
|
-
/* Math.imul(h, m): */
|
|
28
|
-
((h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0xe995) << 16));
|
|
29
|
-
}
|
|
30
|
-
// Handle the last few bytes of the input array
|
|
31
|
-
switch (len) {
|
|
32
|
-
case 3:
|
|
33
|
-
h ^= (str.charCodeAt(i + 2) & 0xff) << 16;
|
|
34
|
-
case 2:
|
|
35
|
-
h ^= (str.charCodeAt(i + 1) & 0xff) << 8;
|
|
36
|
-
case 1:
|
|
37
|
-
h ^= str.charCodeAt(i) & 0xff;
|
|
38
|
-
h =
|
|
39
|
-
/* Math.imul(h, m): */
|
|
40
|
-
(h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0xe995) << 16);
|
|
41
|
-
}
|
|
42
|
-
// Do a few final mixes of the hash to ensure the last few
|
|
43
|
-
// bytes are well-incorporated.
|
|
44
|
-
h ^= h >>> 13;
|
|
45
|
-
h =
|
|
46
|
-
/* Math.imul(h, m): */
|
|
47
|
-
(h & 0xffff) * 0x5bd1e995 + (((h >>> 16) * 0xe995) << 16);
|
|
48
|
-
return ((h ^ (h >>> 15)) >>> 0).toString(36);
|
|
49
|
-
}
|
|
50
|
-
exports.murmur2 = murmur2;
|
package/tsconfig.json
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"module": "CommonJS",
|
|
4
|
-
"alwaysStrict": true,
|
|
5
|
-
"declaration": true,
|
|
6
|
-
"incremental": true,
|
|
7
|
-
"target": "ESNext",
|
|
8
|
-
"outDir": "dist/",
|
|
9
|
-
"moduleResolution": "Node",
|
|
10
|
-
"esModuleInterop": true
|
|
11
|
-
},
|
|
12
|
-
"exclude": ["dist/", "./css.js", "./babelPlugin.js"],
|
|
13
|
-
"include": ["./src/**/*.ts"]
|
|
14
|
-
}
|