@mapsight/vector-style-compiler 9.0.0 → 10.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/README.md +33 -3
- package/dist/cssToRules/mapDeclaration.d.ts +1 -1
- package/dist/cssToRules/mapDeclaration.d.ts.map +1 -1
- package/dist/cssToRules/mapDeclaration.js +2 -2
- package/dist/cssToRules/mapDeclaration.js.map +1 -1
- package/dist/cssToRules/mapRule.d.ts +3 -2
- package/dist/cssToRules/mapRule.d.ts.map +1 -1
- package/dist/cssToRules/mapRule.js +5 -3
- package/dist/cssToRules/mapRule.js.map +1 -1
- package/dist/cssToRules/mapValue.d.ts +3 -0
- package/dist/cssToRules/mapValue.d.ts.map +1 -1
- package/dist/cssToRules/mapValue.js +15 -9
- package/dist/cssToRules/mapValue.js.map +1 -1
- package/dist/cssToRules.d.ts +2 -0
- package/dist/cssToRules.d.ts.map +1 -1
- package/dist/cssToRules.js +4 -3
- package/dist/cssToRules.js.map +1 -1
- package/dist/helpers/Replacer.d.ts.map +1 -1
- package/dist/helpers/Replacer.js +53 -9
- package/dist/helpers/Replacer.js.map +1 -1
- package/dist/helpers/createHelperImportsFromProgram.d.ts +2 -0
- package/dist/helpers/createHelperImportsFromProgram.d.ts.map +1 -0
- package/dist/helpers/createHelperImportsFromProgram.js +9 -0
- package/dist/helpers/createHelperImportsFromProgram.js.map +1 -0
- package/dist/helpers/volatileCalcHelpers.d.ts +3 -0
- package/dist/helpers/volatileCalcHelpers.d.ts.map +1 -0
- package/dist/helpers/volatileCalcHelpers.js +5 -0
- package/dist/helpers/volatileCalcHelpers.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/rulesToTree.d.ts +2 -0
- package/dist/rulesToTree.d.ts.map +1 -1
- package/dist/rulesToTree.js +3 -0
- package/dist/rulesToTree.js.map +1 -1
- package/dist/template.d.ts +2 -1
- package/dist/template.d.ts.map +1 -1
- package/dist/template.js +11 -2
- package/dist/template.js.map +1 -1
- package/dist/template.source.js +61 -41
- package/dist/treeToProgram.d.ts.map +1 -1
- package/dist/treeToProgram.js +12 -4
- package/dist/treeToProgram.js.map +1 -1
- package/package.json +16 -13
- package/dist/template.source.d.ts +0 -3
- package/dist/template.source.d.ts.map +0 -1
- package/dist/template.source.js.map +0 -1
- package/src/data/custom-css-properties.json +0 -200
- package/src/js/cli.ts +0 -98
- package/src/js/cssToRules/mapDeclaration.ts +0 -45
- package/src/js/cssToRules/mapRule.ts +0 -98
- package/src/js/cssToRules/mapSelector.ts +0 -78
- package/src/js/cssToRules/mapSelectorPart.ts +0 -161
- package/src/js/cssToRules/mapValue.ts +0 -84
- package/src/js/cssToRules.ts +0 -49
- package/src/js/helpers/Replacer.ts +0 -50
- package/src/js/helpers/compileDeclarationBlock.ts +0 -60
- package/src/js/helpers/createModulesMapFromDependencies.ts +0 -40
- package/src/js/helpers/ensureObject.ts +0 -17
- package/src/js/helpers/pathToExpression.ts +0 -5
- package/src/js/helpers/uniqueSerialized.ts +0 -7
- package/src/js/index.ts +0 -28
- package/src/js/rulesToTree.ts +0 -83
- package/src/js/template.source.js +0 -56
- package/src/js/template.ts +0 -50
- package/src/js/treeToProgram.ts +0 -220
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import type css from "css";
|
|
2
|
-
|
|
3
|
-
import unique from "@mapsight/lib-js/array/unique";
|
|
4
|
-
import {isTruthy} from "@mapsight/lib-js/boolean";
|
|
5
|
-
import deepMerge from "@mapsight/lib-js/object/deep-extend";
|
|
6
|
-
|
|
7
|
-
import uniqueSerialized from "../helpers/uniqueSerialized.ts";
|
|
8
|
-
import type {DeclarationNode} from "./mapDeclaration.ts";
|
|
9
|
-
import mapDeclaration from "./mapDeclaration.ts";
|
|
10
|
-
import mapSelector, {type Selector} from "./mapSelector.ts";
|
|
11
|
-
|
|
12
|
-
const isDeclaration = (
|
|
13
|
-
val: css.Declaration | css.Comment,
|
|
14
|
-
): val is css.Declaration => val.type === "declaration";
|
|
15
|
-
|
|
16
|
-
export default function mapRule(rule: css.Rule) {
|
|
17
|
-
const declarations =
|
|
18
|
-
rule.declarations
|
|
19
|
-
?.filter(isDeclaration)
|
|
20
|
-
.map(mapDeclaration)
|
|
21
|
-
.filter((a) => !!a) ?? [];
|
|
22
|
-
|
|
23
|
-
const mergedDeclaration: DeclarationNode = deepMerge(
|
|
24
|
-
{},
|
|
25
|
-
...declarations.map((a) => a.declaration),
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
// meta data
|
|
29
|
-
const mergedDeclarationNames = unique(
|
|
30
|
-
declarations.map((declaration) => declaration.__meta.name),
|
|
31
|
-
);
|
|
32
|
-
const mergedStyleProps = unique(
|
|
33
|
-
declarations.flatMap((declaration) => declaration.__meta.styleProps),
|
|
34
|
-
);
|
|
35
|
-
const mergedStylePropExpressions = unique(
|
|
36
|
-
declarations.flatMap(
|
|
37
|
-
(declaration) => declaration.__meta.stylePropExpressions,
|
|
38
|
-
),
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
const selectors = uniqueSerialized(rule.selectors?.map(mapSelector) ?? []);
|
|
42
|
-
const groupedSelectors: Record<string, Array<Selector>> = {};
|
|
43
|
-
selectors.forEach((selector) => {
|
|
44
|
-
const existing = groupedSelectors[selector.group];
|
|
45
|
-
if (Array.isArray(existing)) {
|
|
46
|
-
existing.push(selector);
|
|
47
|
-
} else {
|
|
48
|
-
groupedSelectors[selector.group] = [selector];
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
return Object.keys(groupedSelectors).map((group) => {
|
|
53
|
-
const conditions = uniqueSerialized(groupedSelectors[group]!);
|
|
54
|
-
const mergedStateNames = unique(
|
|
55
|
-
conditions.flatMap((condition) => condition.__meta.stateNames),
|
|
56
|
-
);
|
|
57
|
-
const mergedConditionStyleProps = unique(
|
|
58
|
-
conditions.flatMap((condition) => condition.__meta.styleProps),
|
|
59
|
-
);
|
|
60
|
-
const mergedConditionStylePropExpressions = unique(
|
|
61
|
-
conditions.flatMap(
|
|
62
|
-
(condition) => condition.__meta.stylePropExpressions,
|
|
63
|
-
),
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
conditions: conditions,
|
|
68
|
-
declarations: {
|
|
69
|
-
[group]: mergedDeclaration,
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
__meta: {
|
|
73
|
-
styleNames: unique(
|
|
74
|
-
conditions
|
|
75
|
-
.map((condition) => condition.style)
|
|
76
|
-
.filter(isTruthy),
|
|
77
|
-
),
|
|
78
|
-
stateNames: [
|
|
79
|
-
...unique(
|
|
80
|
-
conditions
|
|
81
|
-
.map((condition) => condition.state)
|
|
82
|
-
.filter(isTruthy),
|
|
83
|
-
),
|
|
84
|
-
...mergedStateNames,
|
|
85
|
-
],
|
|
86
|
-
groupNames: unique(
|
|
87
|
-
conditions.map((condition) => condition.group),
|
|
88
|
-
),
|
|
89
|
-
declarationNames: mergedDeclarationNames,
|
|
90
|
-
styleProps: [...mergedConditionStyleProps, ...mergedStyleProps],
|
|
91
|
-
stylePropExpressions: [
|
|
92
|
-
...mergedConditionStylePropExpressions,
|
|
93
|
-
...mergedStylePropExpressions,
|
|
94
|
-
],
|
|
95
|
-
},
|
|
96
|
-
};
|
|
97
|
-
});
|
|
98
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import unique from "@mapsight/lib-js/array/unique";
|
|
2
|
-
import {isTruthy} from "@mapsight/lib-js/boolean";
|
|
3
|
-
import {ensureNonNullable} from "@mapsight/lib-js/nonNullable";
|
|
4
|
-
|
|
5
|
-
import mapSelectorPart from "./mapSelectorPart.ts";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Finds
|
|
9
|
-
* A) Words incl. whitespace enclosed by matching single quotes ('), square brackets ([,]), not (:not(,)) or matching double quotes (") and
|
|
10
|
-
* B) Words (groups of non-whitespace characters)
|
|
11
|
-
*
|
|
12
|
-
* @type {RegExp}
|
|
13
|
-
*/
|
|
14
|
-
const REGEX_SELECTOR_PART: RegExp = /('.*?'|\[.*?]|:not\(.*?\)|".*?"|\S+)/g;
|
|
15
|
-
|
|
16
|
-
export type Selector = ReturnType<typeof mapSelector>;
|
|
17
|
-
|
|
18
|
-
export default function mapSelector(selector: string) {
|
|
19
|
-
const selectorParts = ensureNonNullable(selector.match(REGEX_SELECTOR_PART))
|
|
20
|
-
.map((part) => mapSelectorPart(part))
|
|
21
|
-
.filter(isTruthy);
|
|
22
|
-
const checks = unique(
|
|
23
|
-
selectorParts.map((a) => "check" in a && a.check).filter(isTruthy),
|
|
24
|
-
);
|
|
25
|
-
const mergedStateNames = unique(
|
|
26
|
-
selectorParts
|
|
27
|
-
.map((part) =>
|
|
28
|
-
"stateNames" in part.__meta
|
|
29
|
-
? part.__meta.stateNames
|
|
30
|
-
: undefined,
|
|
31
|
-
)
|
|
32
|
-
.filter(isTruthy)
|
|
33
|
-
.flat(),
|
|
34
|
-
);
|
|
35
|
-
const mergedStyleProps = unique(
|
|
36
|
-
selectorParts
|
|
37
|
-
.map((part) =>
|
|
38
|
-
"styleProps" in part.__meta
|
|
39
|
-
? part.__meta.styleProps
|
|
40
|
-
: undefined,
|
|
41
|
-
)
|
|
42
|
-
.filter(isTruthy)
|
|
43
|
-
.flat(),
|
|
44
|
-
);
|
|
45
|
-
const mergedStylePropExpressions = unique(
|
|
46
|
-
selectorParts
|
|
47
|
-
.map((part) =>
|
|
48
|
-
"stylePropExpressions" in part.__meta
|
|
49
|
-
? part.__meta.stylePropExpressions
|
|
50
|
-
: undefined,
|
|
51
|
-
)
|
|
52
|
-
.filter(isTruthy)
|
|
53
|
-
.flat(),
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
let style;
|
|
57
|
-
let state;
|
|
58
|
-
let group;
|
|
59
|
-
for (const part of selectorParts) {
|
|
60
|
-
if (!state && "group" in part) group = part.group;
|
|
61
|
-
if (!style && "style" in part) style = part.style;
|
|
62
|
-
if (!state && "state" in part) state = part.state;
|
|
63
|
-
if (state && style && group) break;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return {
|
|
67
|
-
style: style,
|
|
68
|
-
state: state,
|
|
69
|
-
group: group || "default",
|
|
70
|
-
checks: checks?.length ? checks : undefined,
|
|
71
|
-
|
|
72
|
-
__meta: {
|
|
73
|
-
stateNames: mergedStateNames,
|
|
74
|
-
styleProps: mergedStyleProps,
|
|
75
|
-
stylePropExpressions: mergedStylePropExpressions,
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
|
-
}
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import trimQuotes from "@mapsight/lib-js/string/trimQuotes";
|
|
2
|
-
|
|
3
|
-
import mapValue from "./mapValue.ts";
|
|
4
|
-
|
|
5
|
-
type JsCheck = {
|
|
6
|
-
type: "js";
|
|
7
|
-
expression: string;
|
|
8
|
-
negate: boolean;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
type GeometryTypeCheck = {
|
|
12
|
-
type: "geometryType";
|
|
13
|
-
value: string;
|
|
14
|
-
negate: boolean;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
type ValueCheck = {
|
|
18
|
-
type: "value";
|
|
19
|
-
target: "props" | "env";
|
|
20
|
-
path: string[];
|
|
21
|
-
value?: string | number | null;
|
|
22
|
-
negate: boolean;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export type Check = JsCheck | GeometryTypeCheck | ValueCheck;
|
|
26
|
-
|
|
27
|
-
function mapAttributeSelectorPart(
|
|
28
|
-
part: string,
|
|
29
|
-
negate = false,
|
|
30
|
-
): {
|
|
31
|
-
check: Check;
|
|
32
|
-
__meta: {
|
|
33
|
-
styleProps?: string[];
|
|
34
|
-
stylePropExpressions?: string[];
|
|
35
|
-
stateNames?: string[];
|
|
36
|
-
};
|
|
37
|
-
} {
|
|
38
|
-
const operands = part
|
|
39
|
-
.slice(1, -1) // remove square brackets
|
|
40
|
-
.split("="); // split by first equal sign
|
|
41
|
-
let leftHandOperand = operands?.shift()?.trim() || "";
|
|
42
|
-
const rightHandOperand = operands.length
|
|
43
|
-
? trimQuotes(operands.join("=").trim())
|
|
44
|
-
: undefined;
|
|
45
|
-
|
|
46
|
-
// special case: js expression
|
|
47
|
-
if (leftHandOperand.startsWith("|js")) {
|
|
48
|
-
return {
|
|
49
|
-
check: {
|
|
50
|
-
type: "js",
|
|
51
|
-
expression: rightHandOperand || "",
|
|
52
|
-
negate,
|
|
53
|
-
},
|
|
54
|
-
__meta: {},
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// special case: geometry type
|
|
59
|
-
if (leftHandOperand === "geometry|type") {
|
|
60
|
-
const {value, __meta: valueMeta} = mapValue(rightHandOperand);
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
check: {
|
|
64
|
-
type: "geometryType",
|
|
65
|
-
value: String(value ?? ""),
|
|
66
|
-
negate,
|
|
67
|
-
},
|
|
68
|
-
__meta: valueMeta,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
let target: "props" | "env" = "props";
|
|
73
|
-
|
|
74
|
-
// env target
|
|
75
|
-
if (leftHandOperand.startsWith("env|")) {
|
|
76
|
-
target = "env";
|
|
77
|
-
leftHandOperand = leftHandOperand.slice(4);
|
|
78
|
-
} else if (leftHandOperand.startsWith("props|")) {
|
|
79
|
-
// trim optional prefix
|
|
80
|
-
leftHandOperand = leftHandOperand.slice(6);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// kebab case to dot separated string
|
|
84
|
-
const path = leftHandOperand.split("-");
|
|
85
|
-
|
|
86
|
-
// keep track of props used for styling
|
|
87
|
-
let stateNames: string[] = [];
|
|
88
|
-
let styleProps: string[] = [];
|
|
89
|
-
let stylePropExpressions: string[] = [];
|
|
90
|
-
|
|
91
|
-
if (target === "props") {
|
|
92
|
-
styleProps.push(path[0]!);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
let value = undefined;
|
|
96
|
-
if (rightHandOperand !== undefined) {
|
|
97
|
-
const mappedValue = mapValue(rightHandOperand);
|
|
98
|
-
value = mappedValue.value;
|
|
99
|
-
styleProps = styleProps.concat(mappedValue.__meta.styleProps);
|
|
100
|
-
stylePropExpressions = stylePropExpressions.concat(
|
|
101
|
-
mappedValue.__meta.stylePropExpressions,
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
if (leftHandOperand === "state") {
|
|
105
|
-
stateNames = [rightHandOperand];
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
check: {
|
|
111
|
-
type: "value",
|
|
112
|
-
target,
|
|
113
|
-
path,
|
|
114
|
-
value,
|
|
115
|
-
negate,
|
|
116
|
-
},
|
|
117
|
-
__meta: {
|
|
118
|
-
stateNames: stateNames,
|
|
119
|
-
styleProps: styleProps,
|
|
120
|
-
stylePropExpressions: stylePropExpressions,
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export default function mapSelectorPart(part: string, negate = false) {
|
|
126
|
-
// Handle :not(...) negation using recursion
|
|
127
|
-
if (part.startsWith(":not(") && part.endsWith(")")) {
|
|
128
|
-
const inner = part.slice(5, -1).trim(); // remove :not( and )
|
|
129
|
-
return mapSelectorPart(inner, !negate);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const firstLetter = part.charAt(0);
|
|
133
|
-
if (firstLetter === "[") {
|
|
134
|
-
return mapAttributeSelectorPart(part, negate);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (negate) {
|
|
138
|
-
throw new Error(
|
|
139
|
-
"Cannot negate selector part [" + part + "] with :not().",
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (firstLetter === "*") {
|
|
144
|
-
return {__meta: {}} as const;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const rest = part.slice(1);
|
|
148
|
-
if (firstLetter === ":") {
|
|
149
|
-
return {state: rest, __meta: {}} as const;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (firstLetter === "#") {
|
|
153
|
-
return {style: rest, __meta: {}} as const;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (firstLetter === ".") {
|
|
157
|
-
return {group: rest, __meta: {}} as const;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return null;
|
|
161
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import uniq from "lodash/uniq.js";
|
|
2
|
-
|
|
3
|
-
import {ensureNonNullable} from "@mapsight/lib-js/nonNullable";
|
|
4
|
-
import trimQuotes from "@mapsight/lib-js/string/trimQuotes";
|
|
5
|
-
import isNumberLike from "@mapsight/lib-js/types/isNumberLike";
|
|
6
|
-
|
|
7
|
-
import type {ReplacerFn} from "../helpers/Replacer.ts";
|
|
8
|
-
import Replacer from "../helpers/Replacer.ts";
|
|
9
|
-
import pathToExpression from "../helpers/pathToExpression.ts";
|
|
10
|
-
|
|
11
|
-
const replace: ReplacerFn = (_match, parameter) => {
|
|
12
|
-
const [search, replacement, subject] = parameter
|
|
13
|
-
.split(",")
|
|
14
|
-
.map((a) => a.trim())
|
|
15
|
-
.map(trimQuotes);
|
|
16
|
-
|
|
17
|
-
return `' + replace('${search}', '${replacement}', '${subject}') + '`;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const calc: ReplacerFn = (_match, parameter, replacer) => {
|
|
21
|
-
// parameter is e.g. "%%%0%%% * 5"
|
|
22
|
-
// Resolve the inner tags back to their actual string representations
|
|
23
|
-
let resolvedParam = replacer.replace(parameter);
|
|
24
|
-
|
|
25
|
-
// Clean up the string concatenations generated by the earlier `attr` pass.
|
|
26
|
-
// It extracts `props['test']` out of `' + props['test'] + '`
|
|
27
|
-
resolvedParam = resolvedParam.replace(/' \+ (.*?) \+ '/g, "$1");
|
|
28
|
-
|
|
29
|
-
return `' + (${resolvedParam}) + '`;
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export default function mapValue(
|
|
33
|
-
value: string | number | null | undefined | boolean,
|
|
34
|
-
) {
|
|
35
|
-
const meta = {
|
|
36
|
-
styleProps: [] as Array<string>,
|
|
37
|
-
stylePropExpressions: [] as Array<string>,
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
if (!value) {
|
|
41
|
-
return {value: null, __meta: meta};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
let valueStr = String(value);
|
|
45
|
-
if (isNumberLike(value)) {
|
|
46
|
-
return {value: parseFloat(valueStr), __meta: meta};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
valueStr = trimQuotes(valueStr);
|
|
50
|
-
const replacer = new Replacer([
|
|
51
|
-
[
|
|
52
|
-
"attr",
|
|
53
|
-
(_match, parameter) => {
|
|
54
|
-
const isEnv = parameter.startsWith("--env-");
|
|
55
|
-
const parameters = parameter.split("-");
|
|
56
|
-
|
|
57
|
-
if (isEnv) {
|
|
58
|
-
const restParameters = parameters.slice(3);
|
|
59
|
-
return `' + ${pathToExpression("env", restParameters)} + '`;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const expression = pathToExpression("props", parameters);
|
|
63
|
-
meta.styleProps.push(ensureNonNullable(parameters[0]));
|
|
64
|
-
meta.stylePropExpressions.push(expression);
|
|
65
|
-
return `' + ${expression} + '`;
|
|
66
|
-
},
|
|
67
|
-
],
|
|
68
|
-
["replace", replace],
|
|
69
|
-
["calc", calc],
|
|
70
|
-
]);
|
|
71
|
-
valueStr = replacer.execute(valueStr);
|
|
72
|
-
valueStr = valueStr.replace(/'/g, "\\'");
|
|
73
|
-
valueStr = replacer.replace(valueStr);
|
|
74
|
-
valueStr = `'${valueStr}'`;
|
|
75
|
-
|
|
76
|
-
return {
|
|
77
|
-
value: valueStr,
|
|
78
|
-
|
|
79
|
-
__meta: {
|
|
80
|
-
styleProps: uniq(meta.styleProps),
|
|
81
|
-
stylePropExpressions: uniq(meta.stylePropExpressions),
|
|
82
|
-
},
|
|
83
|
-
} as const;
|
|
84
|
-
}
|
package/src/js/cssToRules.ts
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import type css from "css";
|
|
2
|
-
import {parse} from "css";
|
|
3
|
-
|
|
4
|
-
import unique from "@mapsight/lib-js/array/unique";
|
|
5
|
-
|
|
6
|
-
import mapRule from "./cssToRules/mapRule.ts";
|
|
7
|
-
|
|
8
|
-
type Meta = Record<string, unknown>;
|
|
9
|
-
|
|
10
|
-
const collectMeta = <
|
|
11
|
-
TMeta extends Meta,
|
|
12
|
-
TMetaKey extends keyof TMeta = keyof Meta,
|
|
13
|
-
>(
|
|
14
|
-
vals: Array<{__meta: TMeta}>,
|
|
15
|
-
key: TMetaKey,
|
|
16
|
-
) => {
|
|
17
|
-
const values = vals.map((v) => v.__meta[key]).flat();
|
|
18
|
-
|
|
19
|
-
return unique(values);
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
function isRule(val: css.Rule | css.Comment | css.AtRule): val is css.Rule {
|
|
23
|
-
return val.type === "rule";
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// types: rule,
|
|
27
|
-
// ignored types: media, stylesheet, comment, charset, custom-media, document, font-face,
|
|
28
|
-
// import, keyframes, keyframe, media, namespace, page, supports
|
|
29
|
-
function cssOmToRules(om: css.Stylesheet) {
|
|
30
|
-
return om.stylesheet?.rules.filter(isRule).flatMap(mapRule) ?? [];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export type Rules = ReturnType<typeof cssToRules>;
|
|
34
|
-
|
|
35
|
-
export default function cssToRules(content: string) {
|
|
36
|
-
const om = parse(content);
|
|
37
|
-
const rules = cssOmToRules(om);
|
|
38
|
-
return {
|
|
39
|
-
rules,
|
|
40
|
-
__meta: {
|
|
41
|
-
styleNames: collectMeta(rules, "styleNames"),
|
|
42
|
-
stateNames: collectMeta(rules, "stateNames"),
|
|
43
|
-
groupNames: collectMeta(rules, "groupNames"),
|
|
44
|
-
declarationNames: collectMeta(rules, "declarationNames"),
|
|
45
|
-
styleProps: collectMeta(rules, "styleProps"),
|
|
46
|
-
stylePropExpressions: collectMeta(rules, "stylePropExpressions"),
|
|
47
|
-
},
|
|
48
|
-
};
|
|
49
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
const RE_FUNCTION_PARAMETER = "([^\\)]*?)";
|
|
2
|
-
const RE_DOUBLE_QUOTES_IN_PARIS_ONLY = '(?:(?:[^"]*"){2})*[^"]*$';
|
|
3
|
-
const TAG_DELIMITER = "%%%";
|
|
4
|
-
|
|
5
|
-
export type ReplacerFn = (
|
|
6
|
-
match: string,
|
|
7
|
-
parameter: string,
|
|
8
|
-
replacer: Replacer,
|
|
9
|
-
) => string;
|
|
10
|
-
|
|
11
|
-
export default class Replacer {
|
|
12
|
-
private replacementCounter: number = 0;
|
|
13
|
-
private replacements: Array<{tag: string; replacement: string}> = [];
|
|
14
|
-
private functions: Array<(val: string) => string> = [];
|
|
15
|
-
|
|
16
|
-
constructor(functions: Array<[string, ReplacerFn]> = []) {
|
|
17
|
-
functions.forEach(([functionName, replacer]) =>
|
|
18
|
-
this.addFunction(functionName, replacer),
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
addFunction(functionName: string, replacer: ReplacerFn) {
|
|
23
|
-
const regex = new RegExp(
|
|
24
|
-
`(${functionName}\\(${RE_FUNCTION_PARAMETER}\\))(?=${RE_DOUBLE_QUOTES_IN_PARIS_ONLY})`,
|
|
25
|
-
"ig",
|
|
26
|
-
);
|
|
27
|
-
const fn = (value: string): string =>
|
|
28
|
-
value.replace(regex, (match, _, parameter) => {
|
|
29
|
-
const tag =
|
|
30
|
-
TAG_DELIMITER + this.replacementCounter++ + TAG_DELIMITER;
|
|
31
|
-
const replacement = replacer(match, parameter as string, this);
|
|
32
|
-
this.replacements.push({tag, replacement});
|
|
33
|
-
|
|
34
|
-
return tag;
|
|
35
|
-
});
|
|
36
|
-
this.functions.push(fn);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
execute(value: string): string {
|
|
40
|
-
return this.functions.reduce((acc, fn) => fn(acc), value);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
replace(value: string): string {
|
|
44
|
-
return this.replacements.reduceRight(
|
|
45
|
-
(acc, {tag, replacement}) =>
|
|
46
|
-
acc.replace(new RegExp(tag, "g"), replacement),
|
|
47
|
-
value,
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
export type NodeValue = {value: string | number | boolean | null};
|
|
2
|
-
|
|
3
|
-
export type BlockTree = {
|
|
4
|
-
[property: string]: BlockTree | NodeValue | null;
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
const isValueNode = (node: unknown): node is NodeValue =>
|
|
8
|
-
node !== null && typeof node === "object" && "value" in node;
|
|
9
|
-
|
|
10
|
-
const getPropertyAccess = (key: string): string => {
|
|
11
|
-
const isValidIdentifier = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(key);
|
|
12
|
-
return isValidIdentifier ? `.${key}` : `["${key}"]`;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Compiles a parsed Style AST block into highly optimized JS assignments.
|
|
17
|
-
* @param ast The parsed style block
|
|
18
|
-
* @param rootName The name of the base object (default: "declaration")
|
|
19
|
-
* @param indent String used for indentation
|
|
20
|
-
*/
|
|
21
|
-
export function compileDeclarationBlock(
|
|
22
|
-
ast: BlockTree,
|
|
23
|
-
rootName: string = "declaration",
|
|
24
|
-
indent: string = " ",
|
|
25
|
-
): string {
|
|
26
|
-
const lines: string[] = [];
|
|
27
|
-
|
|
28
|
-
function traverse(node: BlockTree, parentRef: string, path: string[]) {
|
|
29
|
-
for (const [key, child] of Object.entries(node)) {
|
|
30
|
-
const access = getPropertyAccess(key);
|
|
31
|
-
|
|
32
|
-
if (child === null) {
|
|
33
|
-
lines.push(`${indent}${parentRef}${access} = null;`);
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
if (isValueNode(child)) {
|
|
38
|
-
lines.push(
|
|
39
|
-
`${indent}${parentRef}${access} = { value: ${child.value} };`,
|
|
40
|
-
);
|
|
41
|
-
} else {
|
|
42
|
-
const newPath = [...path, key];
|
|
43
|
-
const cacheVar =
|
|
44
|
-
"_" +
|
|
45
|
-
newPath
|
|
46
|
-
.map((p) => p.replace(/[^a-zA-Z0-9]/g, ""))
|
|
47
|
-
.join("_");
|
|
48
|
-
|
|
49
|
-
lines.push(
|
|
50
|
-
`${indent}const ${cacheVar} = (${indent}${parentRef}${access} ??= {});`,
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
traverse(child, cacheVar, newPath);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
traverse(ast, rootName, []);
|
|
59
|
-
return lines.join("");
|
|
60
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import uniq from "lodash/uniq.js";
|
|
2
|
-
|
|
3
|
-
import {isTruthy} from "@mapsight/lib-js/boolean";
|
|
4
|
-
|
|
5
|
-
const IMPORT_PREFIX = "__vectorStyle_";
|
|
6
|
-
|
|
7
|
-
const OL_IMPORT_PATH_BY_NAME: Record<string, string> = {
|
|
8
|
-
style: "ol/style/Style",
|
|
9
|
-
image: "ol/style/Image",
|
|
10
|
-
icon: "ol/style/Icon",
|
|
11
|
-
circle: "ol/style/Circle",
|
|
12
|
-
stroke: "ol/style/Stroke",
|
|
13
|
-
text: "ol/style/Text",
|
|
14
|
-
fill: "ol/style/Fill",
|
|
15
|
-
regularShape: "ol/style/RegularShape",
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export default function createModulesMapFromDependencies(
|
|
19
|
-
declarationNames: Array<string>,
|
|
20
|
-
) {
|
|
21
|
-
// @TODO: Find a better way?:
|
|
22
|
-
// special case for circle. we cannot detect fill and stroke if they are used in circle (circle-stroke-color etc.)
|
|
23
|
-
// this assumes a circle uses fill and stroke (which it might not!)
|
|
24
|
-
if (declarationNames.indexOf("circle") > -1) {
|
|
25
|
-
declarationNames.push("stroke");
|
|
26
|
-
declarationNames.push("fill");
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const names = uniq(
|
|
30
|
-
declarationNames
|
|
31
|
-
.concat(["style"]) /* we always need at least ol.style.Style */
|
|
32
|
-
.filter(isTruthy),
|
|
33
|
-
).filter((name) => OL_IMPORT_PATH_BY_NAME[name]);
|
|
34
|
-
|
|
35
|
-
// prettier-ignore
|
|
36
|
-
return {
|
|
37
|
-
imports: names.map(name => `import ${IMPORT_PREFIX}${name} from '${OL_IMPORT_PATH_BY_NAME[name]}';`).join('\n'),
|
|
38
|
-
map: `{${names.map(name => `${name}: ${IMPORT_PREFIX}${name},`).join('\n')}}`,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/** Replaces the value if it's falsy. */
|
|
2
|
-
export default function ensureObject<T extends object, K extends keyof T>(
|
|
3
|
-
parent: T,
|
|
4
|
-
key: K,
|
|
5
|
-
base: T[K],
|
|
6
|
-
): T[K] {
|
|
7
|
-
if (parent === null || parent === undefined) {
|
|
8
|
-
return base;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
if (parent[key]) {
|
|
12
|
-
return parent[key];
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
parent[key] = base;
|
|
16
|
-
return base;
|
|
17
|
-
}
|
package/src/js/index.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import cssToRules from "./cssToRules.ts";
|
|
2
|
-
import rulesToTree from "./rulesToTree.ts";
|
|
3
|
-
import defaultTemplate, {type Template} from "./template.ts";
|
|
4
|
-
import treeToProgram from "./treeToProgram.ts";
|
|
5
|
-
|
|
6
|
-
export type Options = {
|
|
7
|
-
template?: Template;
|
|
8
|
-
additionalData?: Record<string, string | number>;
|
|
9
|
-
baseIndent?: number;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export default function compileMapsightVectorStyle(
|
|
13
|
-
content: string,
|
|
14
|
-
{
|
|
15
|
-
template = defaultTemplate,
|
|
16
|
-
additionalData = {},
|
|
17
|
-
baseIndent = 2,
|
|
18
|
-
}: Options = {},
|
|
19
|
-
) {
|
|
20
|
-
const rules = cssToRules(content);
|
|
21
|
-
const tree = rulesToTree(rules.rules);
|
|
22
|
-
return template({
|
|
23
|
-
__meta: rules.__meta,
|
|
24
|
-
program1: treeToProgram(tree, "hash", baseIndent),
|
|
25
|
-
program2: treeToProgram(tree, "declaration", baseIndent),
|
|
26
|
-
...additionalData,
|
|
27
|
-
});
|
|
28
|
-
}
|