@fastkit/plugboy 0.1.5 → 0.1.7

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.
@@ -0,0 +1,74 @@
1
+ // src/postcss/plugins/combine-rules.ts
2
+ import postcss, { Root } from "postcss";
3
+ function buildFilterFn(spec) {
4
+ return (rule) => {
5
+ if (typeof spec === "function")
6
+ return spec(rule);
7
+ const { selector } = rule;
8
+ return spec.some((target) => {
9
+ if (typeof target === "string")
10
+ return selector === target;
11
+ return target.test(selector);
12
+ });
13
+ };
14
+ }
15
+ function combineContainerRules(container, filter) {
16
+ const { nodes } = container;
17
+ if (!nodes)
18
+ return;
19
+ const rules = nodes.filter(filter);
20
+ const combinedRules = [];
21
+ rules.forEach((rule) => {
22
+ const { selector } = rule;
23
+ let combinedRule = combinedRules.find((c) => c.selector === selector);
24
+ if (!combinedRule) {
25
+ combinedRule = {
26
+ selector,
27
+ rules: [],
28
+ container: rule
29
+ };
30
+ combinedRules.push(combinedRule);
31
+ }
32
+ combinedRule.rules.push(rule);
33
+ });
34
+ combinedRules.forEach((combinedRule) => {
35
+ combinedRule.rules.forEach((rule, index) => {
36
+ if (index === 0)
37
+ return;
38
+ const { nodes: nodes2 } = rule;
39
+ if (!nodes2)
40
+ return;
41
+ nodes2.forEach((node) => {
42
+ combinedRule.container.append(node.clone());
43
+ });
44
+ rule.remove();
45
+ });
46
+ });
47
+ }
48
+ function combineRules(css, opts) {
49
+ const root = css instanceof Root ? css : postcss.parse(css);
50
+ const filter = buildFilterFn(opts.rules);
51
+ combineContainerRules(root, filter);
52
+ root.walkAtRules((atRule) => {
53
+ combineContainerRules(atRule, filter);
54
+ });
55
+ return root;
56
+ }
57
+ var PLUGIN_NAME = "combine-rules";
58
+ function CombineRules(opts) {
59
+ return {
60
+ postcssPlugin: PLUGIN_NAME,
61
+ Once(root) {
62
+ combineRules(root, opts);
63
+ }
64
+ };
65
+ }
66
+ CombineRules.postcss = true;
67
+ var combine_rules_default = CombineRules;
68
+ export {
69
+ CombineRules,
70
+ combineContainerRules,
71
+ combineRules,
72
+ combine_rules_default as default
73
+ };
74
+ //# sourceMappingURL=combine-rules-25G6HBTY.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/postcss/plugins/combine-rules.ts"],"sourcesContent":["import postcss, { Plugin, Root, Container, Rule } from 'postcss';\n\nexport type RuleFilter = string | RegExp;\n\nexport type RuleSpecFn = (rule: Rule) => boolean;\n\nexport type RuleSpec = RuleFilter[] | RuleSpecFn;\n\nexport interface CombineRulesOptions {\n rules: RuleSpec;\n}\n\nfunction buildFilterFn(spec: RuleSpec): RuleSpecFn {\n return (rule) => {\n if (typeof spec === 'function') return spec(rule);\n const { selector } = rule;\n return spec.some((target) => {\n if (typeof target === 'string') return selector === target;\n return target.test(selector);\n });\n };\n}\n\nexport function combineContainerRules(\n container: Container,\n filter: RuleSpecFn,\n) {\n const { nodes } = container;\n if (!nodes) return;\n\n const rules: Rule[] = (nodes as Rule[]).filter(filter);\n const combinedRules: { selector: string; rules: Rule[]; container: Rule }[] =\n [];\n rules.forEach((rule) => {\n const { selector } = rule;\n let combinedRule = combinedRules.find((c) => c.selector === selector);\n if (!combinedRule) {\n combinedRule = {\n selector,\n rules: [],\n container: rule,\n };\n combinedRules.push(combinedRule);\n }\n combinedRule.rules.push(rule);\n });\n\n combinedRules.forEach((combinedRule) => {\n combinedRule.rules.forEach((rule, index) => {\n if (index === 0) return;\n const { nodes } = rule;\n if (!nodes) return;\n nodes.forEach((node) => {\n combinedRule.container.append(node.clone());\n });\n rule.remove();\n });\n });\n}\n\nexport function combineRules(\n css: string | { toString(): string } | Root,\n opts: CombineRulesOptions,\n): Root {\n const root = css instanceof Root ? css : postcss.parse(css);\n const filter = buildFilterFn(opts.rules);\n combineContainerRules(root, filter);\n root.walkAtRules((atRule) => {\n combineContainerRules(atRule, filter);\n });\n return root;\n}\n\nconst PLUGIN_NAME = 'combine-rules';\n\nexport function CombineRules(opts: CombineRulesOptions): Plugin {\n return {\n postcssPlugin: PLUGIN_NAME,\n Once(root) {\n combineRules(root, opts);\n },\n };\n}\n\nCombineRules.postcss = true;\n\nexport default CombineRules;\n"],"mappings":";AAAA,OAAO,WAAmB,YAA6B;AAYvD,SAAS,cAAc,MAA4B;AACjD,SAAO,CAAC,SAAS;AACf,QAAI,OAAO,SAAS;AAAY,aAAO,KAAK,IAAI;AAChD,UAAM,EAAE,SAAS,IAAI;AACrB,WAAO,KAAK,KAAK,CAAC,WAAW;AAC3B,UAAI,OAAO,WAAW;AAAU,eAAO,aAAa;AACpD,aAAO,OAAO,KAAK,QAAQ;AAAA,IAC7B,CAAC;AAAA,EACH;AACF;AAEO,SAAS,sBACd,WACA,QACA;AACA,QAAM,EAAE,MAAM,IAAI;AAClB,MAAI,CAAC;AAAO;AAEZ,QAAM,QAAiB,MAAiB,OAAO,MAAM;AACrD,QAAM,gBACJ,CAAC;AACH,QAAM,QAAQ,CAAC,SAAS;AACtB,UAAM,EAAE,SAAS,IAAI;AACrB,QAAI,eAAe,cAAc,KAAK,CAAC,MAAM,EAAE,aAAa,QAAQ;AACpE,QAAI,CAAC,cAAc;AACjB,qBAAe;AAAA,QACb;AAAA,QACA,OAAO,CAAC;AAAA,QACR,WAAW;AAAA,MACb;AACA,oBAAc,KAAK,YAAY;AAAA,IACjC;AACA,iBAAa,MAAM,KAAK,IAAI;AAAA,EAC9B,CAAC;AAED,gBAAc,QAAQ,CAAC,iBAAiB;AACtC,iBAAa,MAAM,QAAQ,CAAC,MAAM,UAAU;AAC1C,UAAI,UAAU;AAAG;AACjB,YAAM,EAAE,OAAAA,OAAM,IAAI;AAClB,UAAI,CAACA;AAAO;AACZ,MAAAA,OAAM,QAAQ,CAAC,SAAS;AACtB,qBAAa,UAAU,OAAO,KAAK,MAAM,CAAC;AAAA,MAC5C,CAAC;AACD,WAAK,OAAO;AAAA,IACd,CAAC;AAAA,EACH,CAAC;AACH;AAEO,SAAS,aACd,KACA,MACM;AACN,QAAM,OAAO,eAAe,OAAO,MAAM,QAAQ,MAAM,GAAG;AAC1D,QAAM,SAAS,cAAc,KAAK,KAAK;AACvC,wBAAsB,MAAM,MAAM;AAClC,OAAK,YAAY,CAAC,WAAW;AAC3B,0BAAsB,QAAQ,MAAM;AAAA,EACtC,CAAC;AACD,SAAO;AACT;AAEA,IAAM,cAAc;AAEb,SAAS,aAAa,MAAmC;AAC9D,SAAO;AAAA,IACL,eAAe;AAAA,IACf,KAAK,MAAM;AACT,mBAAa,MAAM,IAAI;AAAA,IACzB;AAAA,EACF;AACF;AAEA,aAAa,UAAU;AAEvB,IAAO,wBAAQ;","names":["nodes"]}
@@ -0,0 +1,67 @@
1
+ // src/postcss/plugins/optimize-layer.ts
2
+ import postcss, { Root } from "postcss";
3
+ function resolveFilter(spec, defaults) {
4
+ if (!spec)
5
+ return () => defaults;
6
+ if (typeof spec === "function")
7
+ return spec;
8
+ const conditions = Array.isArray(spec) ? spec : [spec];
9
+ return (layerName) => {
10
+ return conditions.some((condition) => {
11
+ return typeof condition === "string" ? layerName.includes(condition) : condition.test(layerName);
12
+ });
13
+ };
14
+ }
15
+ function optimizeLayer(css, opts = {}) {
16
+ const root = css instanceof Root ? css : postcss.parse(css);
17
+ const layers = [];
18
+ const include = resolveFilter(opts.include, true);
19
+ const exclude = resolveFilter(opts.exclude, false);
20
+ const getLayer = (layerRule) => {
21
+ const name = layerRule.params;
22
+ const layer = layers.find((layer2) => layer2.name === name);
23
+ if (layer)
24
+ return {
25
+ layer,
26
+ atFirst: false
27
+ };
28
+ const created = { name, rule: layerRule };
29
+ layers.push(created);
30
+ return {
31
+ layer: created,
32
+ atFirst: true
33
+ };
34
+ };
35
+ root.walkAtRules("layer", (layerRule) => {
36
+ if (layerRule.parent?.type !== "root")
37
+ return;
38
+ const { params, nodes } = layerRule;
39
+ if (!nodes || !include(params, layerRule) || exclude(params, layerRule))
40
+ return;
41
+ const { layer, atFirst } = getLayer(layerRule);
42
+ if (atFirst)
43
+ return;
44
+ layerRule.nodes.forEach((node) => {
45
+ layer.rule.append(node.clone());
46
+ });
47
+ layerRule.remove();
48
+ });
49
+ return root;
50
+ }
51
+ var PLUGIN_NAME = "optimize-layer";
52
+ function OptimizeLayer(opts) {
53
+ return {
54
+ postcssPlugin: PLUGIN_NAME,
55
+ Once(root) {
56
+ optimizeLayer(root, opts);
57
+ }
58
+ };
59
+ }
60
+ OptimizeLayer.postcss = true;
61
+ var optimize_layer_default = OptimizeLayer;
62
+ export {
63
+ OptimizeLayer,
64
+ optimize_layer_default as default,
65
+ optimizeLayer
66
+ };
67
+ //# sourceMappingURL=optimize-layer-EUG2USFC.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/postcss/plugins/optimize-layer.ts"],"sourcesContent":["import postcss, { AtRule, Plugin, Root } from 'postcss';\n\ntype Filter = (layerName: string, rule: AtRule) => boolean;\n\ntype FilterSpec = string | RegExp | (string | RegExp)[] | Filter;\n\nexport interface OptimizeLayerOptions {\n include?: FilterSpec;\n exclude?: FilterSpec;\n}\n\nfunction resolveFilter(\n spec: FilterSpec | undefined,\n defaults: boolean,\n): Filter {\n if (!spec) return () => defaults;\n if (typeof spec === 'function') return spec;\n const conditions = Array.isArray(spec) ? spec : [spec];\n return (layerName) => {\n return conditions.some((condition) => {\n return typeof condition === 'string'\n ? layerName.includes(condition)\n : condition.test(layerName);\n });\n };\n}\n\ninterface Layer {\n name: string;\n rule: AtRule;\n}\n\nexport function optimizeLayer(\n css: string | { toString(): string } | Root,\n opts: OptimizeLayerOptions = {},\n): Root {\n const root = css instanceof Root ? css : postcss.parse(css);\n const layers: Layer[] = [];\n const include = resolveFilter(opts.include, true);\n const exclude = resolveFilter(opts.exclude, false);\n\n const getLayer = (\n layerRule: AtRule,\n ): {\n layer: Layer;\n atFirst: boolean;\n } => {\n const name = layerRule.params;\n const layer = layers.find((layer) => layer.name === name);\n if (layer)\n return {\n layer,\n atFirst: false,\n };\n\n const created: Layer = { name, rule: layerRule };\n layers.push(created);\n return {\n layer: created,\n atFirst: true,\n };\n };\n\n root.walkAtRules('layer', (layerRule) => {\n if (layerRule.parent?.type !== 'root') return;\n const { params, nodes } = layerRule;\n if (!nodes || !include(params, layerRule) || exclude(params, layerRule))\n return;\n\n const { layer, atFirst } = getLayer(layerRule);\n\n if (atFirst) return;\n\n layerRule.nodes.forEach((node) => {\n layer.rule.append(node.clone());\n });\n\n layerRule.remove();\n });\n\n return root;\n}\n\nconst PLUGIN_NAME = 'optimize-layer';\n\nexport function OptimizeLayer(opts?: OptimizeLayerOptions): Plugin {\n return {\n postcssPlugin: PLUGIN_NAME,\n Once(root) {\n optimizeLayer(root, opts);\n },\n };\n}\n\nOptimizeLayer.postcss = true;\n\nexport default OptimizeLayer;\n"],"mappings":";AAAA,OAAO,WAA2B,YAAY;AAW9C,SAAS,cACP,MACA,UACQ;AACR,MAAI,CAAC;AAAM,WAAO,MAAM;AACxB,MAAI,OAAO,SAAS;AAAY,WAAO;AACvC,QAAM,aAAa,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI;AACrD,SAAO,CAAC,cAAc;AACpB,WAAO,WAAW,KAAK,CAAC,cAAc;AACpC,aAAO,OAAO,cAAc,WACxB,UAAU,SAAS,SAAS,IAC5B,UAAU,KAAK,SAAS;AAAA,IAC9B,CAAC;AAAA,EACH;AACF;AAOO,SAAS,cACd,KACA,OAA6B,CAAC,GACxB;AACN,QAAM,OAAO,eAAe,OAAO,MAAM,QAAQ,MAAM,GAAG;AAC1D,QAAM,SAAkB,CAAC;AACzB,QAAM,UAAU,cAAc,KAAK,SAAS,IAAI;AAChD,QAAM,UAAU,cAAc,KAAK,SAAS,KAAK;AAEjD,QAAM,WAAW,CACf,cAIG;AACH,UAAM,OAAO,UAAU;AACvB,UAAM,QAAQ,OAAO,KAAK,CAACA,WAAUA,OAAM,SAAS,IAAI;AACxD,QAAI;AACF,aAAO;AAAA,QACL;AAAA,QACA,SAAS;AAAA,MACX;AAEF,UAAM,UAAiB,EAAE,MAAM,MAAM,UAAU;AAC/C,WAAO,KAAK,OAAO;AACnB,WAAO;AAAA,MACL,OAAO;AAAA,MACP,SAAS;AAAA,IACX;AAAA,EACF;AAEA,OAAK,YAAY,SAAS,CAAC,cAAc;AACvC,QAAI,UAAU,QAAQ,SAAS;AAAQ;AACvC,UAAM,EAAE,QAAQ,MAAM,IAAI;AAC1B,QAAI,CAAC,SAAS,CAAC,QAAQ,QAAQ,SAAS,KAAK,QAAQ,QAAQ,SAAS;AACpE;AAEF,UAAM,EAAE,OAAO,QAAQ,IAAI,SAAS,SAAS;AAE7C,QAAI;AAAS;AAEb,cAAU,MAAM,QAAQ,CAAC,SAAS;AAChC,YAAM,KAAK,OAAO,KAAK,MAAM,CAAC;AAAA,IAChC,CAAC;AAED,cAAU,OAAO;AAAA,EACnB,CAAC;AAED,SAAO;AACT;AAEA,IAAM,cAAc;AAEb,SAAS,cAAc,MAAqC;AACjE,SAAO;AAAA,IACL,eAAe;AAAA,IACf,KAAK,MAAM;AACT,oBAAc,MAAM,IAAI;AAAA,IAC1B;AAAA,EACF;AACF;AAEA,cAAc,UAAU;AAExB,IAAO,yBAAQ;","names":["layer"]}
@@ -0,0 +1,105 @@
1
+ // src/postcss/plugins/optimize-media.ts
2
+ import postcss, {
3
+ AtRule,
4
+ Root
5
+ } from "postcss";
6
+ function resolveFilter(spec, defaults) {
7
+ if (!spec)
8
+ return () => defaults;
9
+ if (typeof spec === "function")
10
+ return spec;
11
+ const conditions = Array.isArray(spec) ? spec : [spec];
12
+ return (layerName) => {
13
+ return conditions.some((condition) => {
14
+ return typeof condition === "string" ? layerName.includes(condition) : condition.test(layerName);
15
+ });
16
+ };
17
+ }
18
+ function parseMedia(mediaRule) {
19
+ const { params, parent } = mediaRule;
20
+ if (!parent) {
21
+ throw mediaRule.error("@media rules require a parent container.");
22
+ }
23
+ return {
24
+ query: params,
25
+ rule: mediaRule,
26
+ container: parent
27
+ };
28
+ }
29
+ function isSourceMapAnnotation(node) {
30
+ if (!node)
31
+ return false;
32
+ if (node.type !== "comment")
33
+ return false;
34
+ return node.text.toLowerCase().startsWith("# sourcemappingurl=");
35
+ }
36
+ function getSourceMapAnnotation(container) {
37
+ const maybeAnnotation = container.last;
38
+ if (isSourceMapAnnotation(maybeAnnotation)) {
39
+ return maybeAnnotation;
40
+ }
41
+ }
42
+ function optimizeMedia(css, opts = {}) {
43
+ const root = css instanceof Root ? css : postcss.parse(css);
44
+ const include = resolveFilter(opts.include, true);
45
+ const exclude = resolveFilter(opts.exclude, false);
46
+ const sourceMap = getSourceMapAnnotation(root);
47
+ const containerMap = /* @__PURE__ */ new Map();
48
+ root.walkAtRules("media", (mediaRule) => {
49
+ const media = parseMedia(mediaRule);
50
+ if (!mediaRule.nodes || !include(media.query, media) || exclude(media.query, media))
51
+ return;
52
+ let bucket = containerMap.get(media.container);
53
+ if (!bucket) {
54
+ bucket = [];
55
+ containerMap.set(media.container, bucket);
56
+ }
57
+ bucket.push(media);
58
+ });
59
+ containerMap.forEach((bucket, container) => {
60
+ if (opts.sort) {
61
+ bucket.sort(opts.sort);
62
+ }
63
+ const combinedRules = [];
64
+ bucket.forEach((media) => {
65
+ let rule = combinedRules.find((r) => r.query === media.query);
66
+ if (!rule) {
67
+ rule = { query: media.query, medias: [] };
68
+ combinedRules.push(rule);
69
+ }
70
+ rule.medias.push(media);
71
+ });
72
+ combinedRules.forEach((rule) => {
73
+ const nodes = rule.medias.map((media) => media.rule.nodes.map((node) => node.clone())).flat();
74
+ const newAtRule = new AtRule({
75
+ name: "media",
76
+ params: rule.query,
77
+ nodes
78
+ });
79
+ rule.medias.forEach((media) => media.rule.remove());
80
+ container.append(newAtRule);
81
+ });
82
+ });
83
+ containerMap.clear();
84
+ if (sourceMap) {
85
+ root.append(sourceMap);
86
+ }
87
+ return root;
88
+ }
89
+ var PLUGIN_NAME = "optimize-media";
90
+ function OptimizeMedia(opts) {
91
+ return {
92
+ postcssPlugin: PLUGIN_NAME,
93
+ Once(root) {
94
+ optimizeMedia(root, opts);
95
+ }
96
+ };
97
+ }
98
+ OptimizeMedia.postcss = true;
99
+ var optimize_media_default = OptimizeMedia;
100
+ export {
101
+ OptimizeMedia,
102
+ optimize_media_default as default,
103
+ optimizeMedia
104
+ };
105
+ //# sourceMappingURL=optimize-media-VB73IGHI.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/postcss/plugins/optimize-media.ts"],"sourcesContent":["import postcss, {\n AtRule,\n Plugin,\n Root,\n Container,\n ChildNode,\n Comment,\n} from 'postcss';\n\ninterface Media {\n query: string;\n rule: AtRule;\n container: Container;\n}\n\ntype Filter = (query: string, media: Media) => boolean;\n\ntype FilterSpec = string | RegExp | (string | RegExp)[] | Filter;\n\nfunction resolveFilter(\n spec: FilterSpec | undefined,\n defaults: boolean,\n): Filter {\n if (!spec) return () => defaults;\n if (typeof spec === 'function') return spec;\n const conditions = Array.isArray(spec) ? spec : [spec];\n return (layerName) => {\n return conditions.some((condition) => {\n return typeof condition === 'string'\n ? layerName.includes(condition)\n : condition.test(layerName);\n });\n };\n}\n\ntype SortMedia = (a: Media, b: Media) => number;\n\nexport interface OptimizeMediaOptions {\n include?: FilterSpec;\n exclude?: FilterSpec;\n sort?: SortMedia;\n}\n\n// const MEDIA_WIDTH_MATCH_RE = /\\((min|max)-width:(\\d+)px\\)/;\n\n// const MEDIA_RANGE_TYPES = ['min', 'max', 'minmax'] as const;\n\n// type MediaRangeType = typeof MEDIA_RANGE_TYPES[number];\n\n// function getMediaRangeType(range: { min?: number; max?: number }): MediaRangeType {\n// const { min, max } = range;\n// const hasMin = min != null;\n// const hasMax = max != null;\n\n// if (!hasMin && !hasMax) throw new Error('missing amount');\n// if (hasMin && hasMax) return 'minmax';\n// if (hasMin) return 'min';\n// return 'max';\n// }\n\n// function getMediaRangeTypeAndScore(range: { min?: number; max?: number }): {\n// type: MediaRangeType;\n// score: number;\n// } {\n// const type = getMediaRangeType(range);\n// return {\n// type,\n// score: MEDIA_RANGE_TYPES.indexOf(type),\n// }\n// }\n\n// function parseMediaRange(query: string): MediaWidthRange {\n// let min: number | undefined;\n// let max: number | undefined;\n// const chunks = query.split(' and ').map((chunk) => chunk.replace(/\\s/g, ''));\n// chunks.forEach((chunk) => {\n// const widthMatch = chunk.match(MEDIA_WIDTH_MATCH_RE);\n// if (!widthMatch) return;\n// const type = widthMatch[1] as 'min' | 'max';\n// const amount = Number(widthMatch[2]);\n// if (type === 'min') {\n// min = amount;\n// } else if (type === 'max') {\n// max = amount;\n// }\n// });\n// return { min, max };\n// }\n\nfunction parseMedia(mediaRule: AtRule): Media {\n const { params, parent } = mediaRule;\n if (!parent) {\n throw mediaRule.error('@media rules require a parent container.');\n }\n return {\n query: params,\n rule: mediaRule,\n container: parent,\n };\n}\n\nfunction isSourceMapAnnotation(node: ChildNode | undefined): node is Comment {\n if (!node) return false;\n if (node.type !== 'comment') return false;\n return node.text.toLowerCase().startsWith('# sourcemappingurl=');\n}\n\nfunction getSourceMapAnnotation(container: Container): ChildNode | undefined {\n const maybeAnnotation = container.last;\n if (isSourceMapAnnotation(maybeAnnotation)) {\n return maybeAnnotation;\n }\n}\n\nexport function optimizeMedia(\n css: string | { toString(): string } | Root,\n opts: OptimizeMediaOptions = {},\n): Root {\n const root = css instanceof Root ? css : postcss.parse(css);\n const include = resolveFilter(opts.include, true);\n const exclude = resolveFilter(opts.exclude, false);\n\n const sourceMap = getSourceMapAnnotation(root);\n\n const containerMap = new Map<Container, Media[]>();\n\n root.walkAtRules('media', (mediaRule) => {\n const media = parseMedia(mediaRule);\n if (\n !mediaRule.nodes ||\n !include(media.query, media) ||\n exclude(media.query, media)\n )\n return;\n\n let bucket = containerMap.get(media.container);\n if (!bucket) {\n bucket = [];\n containerMap.set(media.container, bucket);\n }\n bucket.push(media);\n });\n\n containerMap.forEach((bucket, container) => {\n if (opts.sort) {\n bucket.sort(opts.sort);\n }\n\n const combinedRules: {\n query: string;\n medias: Media[];\n }[] = [];\n\n bucket.forEach((media) => {\n let rule = combinedRules.find((r) => r.query === media.query);\n if (!rule) {\n rule = { query: media.query, medias: [] };\n combinedRules.push(rule);\n }\n rule.medias.push(media);\n });\n\n combinedRules.forEach((rule) => {\n const nodes = rule.medias\n .map((media) => media.rule.nodes.map((node) => node.clone()))\n .flat();\n const newAtRule = new AtRule({\n name: 'media',\n params: rule.query,\n nodes,\n });\n rule.medias.forEach((media) => media.rule.remove());\n container.append(newAtRule);\n });\n });\n\n containerMap.clear();\n\n if (sourceMap) {\n root.append(sourceMap);\n }\n\n return root;\n}\n\nconst PLUGIN_NAME = 'optimize-media';\n\nexport function OptimizeMedia(opts?: OptimizeMediaOptions): Plugin {\n return {\n postcssPlugin: PLUGIN_NAME,\n Once(root) {\n optimizeMedia(root, opts);\n },\n };\n}\n\nOptimizeMedia.postcss = true;\n\nexport default OptimizeMedia;\n"],"mappings":"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","names":[]}
package/dist/plugboy.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import { Options } from 'tsup';
2
2
  import fs from 'node:fs';
3
3
  import { PackageJson } from 'pkg-types';
4
+ import { AtRule, Container, Rule } from 'postcss';
5
+ import { Options as Options$1 } from 'cssnano';
4
6
  import { CompilerOptions } from 'typescript';
5
7
  import { Plugin as Plugin$1, OutputFile } from 'esbuild';
6
8
 
@@ -175,6 +177,7 @@ declare class PlugboyWorkspace {
175
177
  readonly hooks: BuildedHooks;
176
178
  readonly dtsFiles: string[];
177
179
  readonly dts: NormalizedDTSSettings;
180
+ readonly optimizeCSSOptions: ResolvedOptimizeCSSOptions | false;
178
181
  private _json;
179
182
  get json(): WorkspacePackageJson;
180
183
  constructor(ctx: WorkspaceSetupContext);
@@ -193,6 +196,7 @@ interface ResolvedOptions extends Options {
193
196
  declare class Builder {
194
197
  readonly workspace: PlugboyWorkspace;
195
198
  private _tsupOptions?;
199
+ private _postcssCache?;
196
200
  get entry(): Record<string, string>;
197
201
  get dts(): NormalizedDTSSettings;
198
202
  constructor(workspace: PlugboyWorkspace);
@@ -208,6 +212,8 @@ declare class Builder {
208
212
  normalizeDTSFiles(dtsFiles?: string[]): Promise<void>;
209
213
  emitInlineDTS(): Promise<void>;
210
214
  build(): Promise<void>;
215
+ optimizeCSS(cssFilePath: string): Promise<void>;
216
+ private _handleCSSOutput;
211
217
  }
212
218
 
213
219
  declare function generateWorkspace(workspaceName?: string, cwd?: string): Promise<void>;
@@ -299,6 +305,77 @@ interface NormalizedDTSSettings {
299
305
  declare function normalizeDTSSettings(settings: DTSSettings): NormalizedDTSSettings;
300
306
  declare function mergeDTSSettingsList(...settingsList: (DTSSettings | undefined)[]): NormalizedDTSSettings;
301
307
 
308
+ type Filter$1 = (layerName: string, rule: AtRule) => boolean;
309
+ type FilterSpec$1 = string | RegExp | (string | RegExp)[] | Filter$1;
310
+ interface OptimizeLayerOptions {
311
+ include?: FilterSpec$1;
312
+ exclude?: FilterSpec$1;
313
+ }
314
+
315
+ interface Media {
316
+ query: string;
317
+ rule: AtRule;
318
+ container: Container;
319
+ }
320
+ type Filter = (query: string, media: Media) => boolean;
321
+ type FilterSpec = string | RegExp | (string | RegExp)[] | Filter;
322
+ type SortMedia = (a: Media, b: Media) => number;
323
+ interface OptimizeMediaOptions {
324
+ include?: FilterSpec;
325
+ exclude?: FilterSpec;
326
+ sort?: SortMedia;
327
+ }
328
+
329
+ type RuleFilter = string | RegExp;
330
+ type RuleSpecFn = (rule: Rule) => boolean;
331
+ type RuleSpec = RuleFilter[] | RuleSpecFn;
332
+ interface CombineRulesOptions {
333
+ rules: RuleSpec;
334
+ }
335
+
336
+ /**
337
+ * CSS optimization options
338
+ */
339
+ interface OptimizeCSSOptions {
340
+ /**
341
+ * Layer optimization options
342
+ *
343
+ * Disable the operation with `false`.
344
+ *
345
+ * @default true
346
+ */
347
+ layer?: OptimizeLayerOptions | boolean;
348
+ /**
349
+ * Media Query Optimization Options
350
+ *
351
+ * Disable the operation with `false`.
352
+ *
353
+ * @default true
354
+ */
355
+ media?: OptimizeMediaOptions | boolean;
356
+ /**
357
+ * Combine rules Options
358
+ *
359
+ * If unset, no optimization is performed
360
+ */
361
+ combineRules?: CombineRulesOptions;
362
+ /**
363
+ * Options for [cssnano](https://cssnano.co/)
364
+ *
365
+ * Disable the operation with `false`.
366
+ *
367
+ * @default { preset: ['default', { normalizeWhitespace: false }] }
368
+ */
369
+ cssnano?: Options$1 | boolean;
370
+ }
371
+ interface ResolvedOptimizeCSSOptions {
372
+ layer?: OptimizeLayerOptions;
373
+ media?: OptimizeMediaOptions;
374
+ combineRules?: CombineRulesOptions;
375
+ cssnano?: Options$1;
376
+ }
377
+ declare function resolveOptimizeCSSOptions(options: OptimizeCSSOptions): ResolvedOptimizeCSSOptions;
378
+
302
379
  declare const WORKSPACE_REQUIRED_FIELDS: readonly ["name", "version"];
303
380
  type WorkspaceRequiredField = (typeof WORKSPACE_REQUIRED_FIELDS)[number];
304
381
  /**
@@ -368,11 +445,21 @@ interface UserWorkspaceConfig extends TSUpSyncOptions {
368
445
  * @see {@link DTSSettings}
369
446
  */
370
447
  dts?: DTSSettings;
448
+ /**
449
+ * CSS optimization options
450
+ *
451
+ * Disable the operation with `false`.
452
+ *
453
+ * @default true
454
+ *
455
+ * @see {@link OptimizeCSSOptions}
456
+ */
457
+ optimizeCSS?: OptimizeCSSOptions | boolean;
371
458
  }
372
459
  /**
373
460
  * Workspace Configuration
374
461
  */
375
- interface ResolvedWorkspaceConfig extends Required<Omit<UserWorkspaceConfig, 'entries' | 'hooks' | 'plugins' | 'dts' | TSUpSyncOption>>, TSUpSyncOptions {
462
+ interface ResolvedWorkspaceConfig extends Required<Omit<UserWorkspaceConfig, 'entries' | 'hooks' | 'plugins' | 'dts' | 'optimizeCSS' | TSUpSyncOption>>, TSUpSyncOptions {
376
463
  /**
377
464
  * Configuration of all entries in the workspace
378
465
  *
@@ -394,6 +481,14 @@ interface ResolvedWorkspaceConfig extends Required<Omit<UserWorkspaceConfig, 'en
394
481
  * @see {@link DTSSettings}
395
482
  */
396
483
  dts?: DTSSettings;
484
+ /**
485
+ * CSS optimization options
486
+ *
487
+ * Disable the operation with `false`.
488
+ *
489
+ * @see {@link OptimizeCSSOptions}
490
+ */
491
+ optimizeCSS: OptimizeCSSOptions | false;
397
492
  }
398
493
  type WorkspacePackageJson = RequiredPackageJSON<WorkspaceRequiredField>;
399
494
  /**
@@ -458,6 +553,14 @@ interface WorkspaceSetupContext {
458
553
  * @see {@link NormalizedDTSSettings}
459
554
  */
460
555
  dts: NormalizedDTSSettings;
556
+ /**
557
+ * CSS optimization options
558
+ *
559
+ * Disable the operation with `false`.
560
+ *
561
+ * @see {@link OptimizeCSSOptions}
562
+ */
563
+ optimizeCSS: OptimizeCSSOptions | false;
461
564
  }
462
565
 
463
566
  /** plugboy hook definition */
@@ -577,11 +680,21 @@ interface UserProjectConfig {
577
680
  * @see {@link DTSSettings}
578
681
  */
579
682
  dts?: DTSSettings;
683
+ /**
684
+ * CSS optimization options
685
+ *
686
+ * Disable the operation with `false`.
687
+ *
688
+ * @default true
689
+ *
690
+ * @see {@link OptimizeCSSOptions}
691
+ */
692
+ optimizeCSS?: OptimizeCSSOptions | boolean;
580
693
  }
581
694
  /**
582
695
  * Project Configuration
583
696
  */
584
- interface ResolvedProjectConfig extends Required<Omit<UserProjectConfig, 'scripts' | 'tsconfig' | 'hooks' | 'plugins' | 'dts'>> {
697
+ interface ResolvedProjectConfig extends Required<Omit<UserProjectConfig, 'scripts' | 'tsconfig' | 'hooks' | 'plugins' | 'dts' | 'optimizeCSS'>> {
585
698
  /**
586
699
  * Workspace script templates list
587
700
  * @remarks Used to create a new workspace with the `plugboy gen` CLI command.
@@ -607,6 +720,14 @@ interface ResolvedProjectConfig extends Required<Omit<UserProjectConfig, 'script
607
720
  * @see {@link DTSSettings}
608
721
  */
609
722
  dts?: DTSSettings;
723
+ /**
724
+ * CSS optimization options
725
+ *
726
+ * Disable the operation with `false`.
727
+ *
728
+ * @see {@link OptimizeCSSOptions}
729
+ */
730
+ optimizeCSS: OptimizeCSSOptions | false;
610
731
  }
611
732
  type ProjectPackageJson = RequiredPackageJSON<ProjectRequiredField>;
612
733
  /**
@@ -639,4 +760,4 @@ interface GetWorkspacePackageJsonResult {
639
760
  declare function getWorkspacePackageJson<AllowMissing extends boolean | undefined = false>(searchDir?: string, allowMissing?: AllowMissing): Promise<AllowMissing extends true ? GetWorkspacePackageJsonResult | null : GetWorkspacePackageJsonResult>;
640
761
  declare function findWorkspacePackages(dir: string): Promise<GetWorkspacePackageJsonResult[]>;
641
762
 
642
- export { BuildedHooks, Builder, DTSPreserveTypeSettings, DTSPreserveTypeTarget, DTSSettings, ESBuildPlugin, ESBuildPluginOption, ExposeEntriesSettings, FindConfigResult, GetProjectPackageJsonResult, GetWorkspacePackageJsonResult, HookArgs, HookName, HookReturnType, HookTypes, Listable, NormalizedDTSPreserveTypeSettings, NormalizedDTSPreserveTypeTarget, NormalizedDTSSettings, PROJECT_REQUIRED_FIELDS, Path, PlugboyProject, PlugboyWorkspace, Plugin, ProjectPackageJson, ProjectScriptsTemplate, ProjectSetupContext, RawExposeEntriesSettings, RawWorkspaceEntries, RawWorkspaceEntry, RawWorkspaceEntryObject, ResolvedHooks, ResolvedProjectConfig, ResolvedWorkspaceConfig, TSConfigJSON, TSUP_SYNC_OPTIONS, UnPromisify, UserHooks, UserPluginOption, UserProjectConfig, UserWorkspaceConfig, WORKSPACE_PACKAGE_SYNC_FIELDS, WORKSPACE_REQUIRED_FIELDS, WorkspaceDirs, WorkspaceEntries, WorkspaceEntry, WorkspaceExport, WorkspaceMeta, WorkspaceObjectExport, WorkspacePackageJson, WorkspaceSetupContext, WorkspaceStubLink, WorkspaceStubLinkType, buildHooks, copyDirSync, createHooksDefaults, definePlugin, defineProjectConfig, defineWorkspaceConfig, exposeEntries, extractProjectPlugins, findConfig, findFile, findProjectPlugin, findWorkspacePackages, generateWorkspace, getDirname, getFilename, getProject, getProjectPackageJson, getWorkspace, getWorkspacePackageJson, isFileNotFoundException, isProjectPackageJson, isPromise, isWorkspacePackageJson, loadProjectConfig, loadWorkspaceConfig, mergeDTSSettingsList, normalizeDTSPreserveTypeSettings, normalizeDTSPreserveTypeTarget, normalizeDTSSettings, pathExists, resolveListable, resolveRawExposeEntriesSettings, resolveRawWorkspaceEntries, resolveRawWorkspaceEntry, resolveUserHooks, resolveUserPluginOptions, resolveUserProjectConfig, resolveUserWorkspaceConfig, rmrf, syncWorkspacePackageFields };
763
+ export { BuildedHooks, Builder, DTSPreserveTypeSettings, DTSPreserveTypeTarget, DTSSettings, ESBuildPlugin, ESBuildPluginOption, ExposeEntriesSettings, FindConfigResult, GetProjectPackageJsonResult, GetWorkspacePackageJsonResult, HookArgs, HookName, HookReturnType, HookTypes, Listable, NormalizedDTSPreserveTypeSettings, NormalizedDTSPreserveTypeTarget, NormalizedDTSSettings, OptimizeCSSOptions, PROJECT_REQUIRED_FIELDS, Path, PlugboyProject, PlugboyWorkspace, Plugin, ProjectPackageJson, ProjectScriptsTemplate, ProjectSetupContext, RawExposeEntriesSettings, RawWorkspaceEntries, RawWorkspaceEntry, RawWorkspaceEntryObject, ResolvedHooks, ResolvedOptimizeCSSOptions, ResolvedProjectConfig, ResolvedWorkspaceConfig, TSConfigJSON, TSUP_SYNC_OPTIONS, UnPromisify, UserHooks, UserPluginOption, UserProjectConfig, UserWorkspaceConfig, WORKSPACE_PACKAGE_SYNC_FIELDS, WORKSPACE_REQUIRED_FIELDS, WorkspaceDirs, WorkspaceEntries, WorkspaceEntry, WorkspaceExport, WorkspaceMeta, WorkspaceObjectExport, WorkspacePackageJson, WorkspaceSetupContext, WorkspaceStubLink, WorkspaceStubLinkType, buildHooks, copyDirSync, createHooksDefaults, definePlugin, defineProjectConfig, defineWorkspaceConfig, exposeEntries, extractProjectPlugins, findConfig, findFile, findProjectPlugin, findWorkspacePackages, generateWorkspace, getDirname, getFilename, getProject, getProjectPackageJson, getWorkspace, getWorkspacePackageJson, isFileNotFoundException, isProjectPackageJson, isPromise, isWorkspacePackageJson, loadProjectConfig, loadWorkspaceConfig, mergeDTSSettingsList, normalizeDTSPreserveTypeSettings, normalizeDTSPreserveTypeTarget, normalizeDTSSettings, pathExists, resolveListable, resolveOptimizeCSSOptions, resolveRawExposeEntriesSettings, resolveRawWorkspaceEntries, resolveRawWorkspaceEntry, resolveUserHooks, resolveUserPluginOptions, resolveUserProjectConfig, resolveUserWorkspaceConfig, rmrf, syncWorkspacePackageFields };
package/dist/plugboy.mjs CHANGED
@@ -38,6 +38,7 @@ import {
38
38
  normalizeDTSSettings,
39
39
  pathExists,
40
40
  resolveListable,
41
+ resolveOptimizeCSSOptions,
41
42
  resolveRawExposeEntriesSettings,
42
43
  resolveRawWorkspaceEntries,
43
44
  resolveRawWorkspaceEntry,
@@ -47,7 +48,7 @@ import {
47
48
  resolveUserWorkspaceConfig,
48
49
  rmrf,
49
50
  syncWorkspacePackageFields
50
- } from "./chunk-3ELYX3YK.mjs";
51
+ } from "./chunk-4YNK5IZO.mjs";
51
52
  export {
52
53
  Builder,
53
54
  PROJECT_REQUIRED_FIELDS,
@@ -88,6 +89,7 @@ export {
88
89
  normalizeDTSSettings,
89
90
  pathExists,
90
91
  resolveListable,
92
+ resolveOptimizeCSSOptions,
91
93
  resolveRawExposeEntriesSettings,
92
94
  resolveRawWorkspaceEntries,
93
95
  resolveRawWorkspaceEntry,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fastkit/plugboy",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "repository": {