@mapsight/vector-style-compiler 8.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 +366 -0
- package/bin/vector-style-compiler.js +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +83 -0
- package/dist/cli.js.map +1 -0
- package/dist/cssToRules/mapDeclaration.d.ts +16 -0
- package/dist/cssToRules/mapDeclaration.d.ts.map +1 -0
- package/dist/cssToRules/mapDeclaration.js +24 -0
- package/dist/cssToRules/mapDeclaration.js.map +1 -0
- package/dist/cssToRules/mapRule.d.ts +41 -0
- package/dist/cssToRules/mapRule.d.ts.map +1 -0
- package/dist/cssToRules/mapRule.js +60 -0
- package/dist/cssToRules/mapRule.js.map +1 -0
- package/dist/cssToRules/mapSelector.d.ts +27 -0
- package/dist/cssToRules/mapSelector.d.ts.map +1 -0
- package/dist/cssToRules/mapSelector.js +61 -0
- package/dist/cssToRules/mapSelector.js.map +1 -0
- package/dist/cssToRules/mapSelectorPart.d.ts +48 -0
- package/dist/cssToRules/mapSelectorPart.d.ts.map +1 -0
- package/dist/cssToRules/mapSelectorPart.js +106 -0
- package/dist/cssToRules/mapSelectorPart.js.map +1 -0
- package/dist/cssToRules/mapValue.d.ts +20 -0
- package/dist/cssToRules/mapValue.d.ts.map +1 -0
- package/dist/cssToRules/mapValue.js +67 -0
- package/dist/cssToRules/mapValue.js.map +1 -0
- package/dist/cssToRules.d.ts +50 -0
- package/dist/cssToRules.d.ts.map +1 -0
- package/dist/cssToRules.js +32 -0
- package/dist/cssToRules.js.map +1 -0
- package/dist/helpers/Replacer.d.ts +11 -0
- package/dist/helpers/Replacer.d.ts.map +1 -0
- package/dist/helpers/Replacer.js +28 -0
- package/dist/helpers/Replacer.js.map +1 -0
- package/dist/helpers/compileDeclarationBlock.d.ts +14 -0
- package/dist/helpers/compileDeclarationBlock.d.ts.map +1 -0
- package/dist/helpers/compileDeclarationBlock.js +38 -0
- package/dist/helpers/compileDeclarationBlock.js.map +1 -0
- package/dist/helpers/createModulesMapFromDependencies.d.ts +5 -0
- package/dist/helpers/createModulesMapFromDependencies.d.ts.map +1 -0
- package/dist/helpers/createModulesMapFromDependencies.js +31 -0
- package/dist/helpers/createModulesMapFromDependencies.js.map +1 -0
- package/dist/helpers/ensureObject.d.ts +3 -0
- package/dist/helpers/ensureObject.d.ts.map +1 -0
- package/dist/helpers/ensureObject.js +12 -0
- package/dist/helpers/ensureObject.js.map +1 -0
- package/dist/helpers/pathToExpression.d.ts +2 -0
- package/dist/helpers/pathToExpression.d.ts.map +1 -0
- package/dist/helpers/pathToExpression.js +6 -0
- package/dist/helpers/pathToExpression.js.map +1 -0
- package/dist/helpers/uniqueSerialized.d.ts +2 -0
- package/dist/helpers/uniqueSerialized.d.ts.map +1 -0
- package/dist/helpers/uniqueSerialized.js +5 -0
- package/dist/helpers/uniqueSerialized.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/rulesToTree.d.ts +20 -0
- package/dist/rulesToTree.d.ts.map +1 -0
- package/dist/rulesToTree.js +43 -0
- package/dist/rulesToTree.js.map +1 -0
- package/dist/template.d.ts +16 -0
- package/dist/template.d.ts.map +1 -0
- package/dist/template.js +22 -0
- package/dist/template.js.map +1 -0
- package/dist/template.source.d.ts +3 -0
- package/dist/template.source.d.ts.map +1 -0
- package/dist/template.source.js +50 -0
- package/dist/template.source.js.map +1 -0
- package/dist/treeToProgram.d.ts +3 -0
- package/dist/treeToProgram.d.ts.map +1 -0
- package/dist/treeToProgram.js +158 -0
- package/dist/treeToProgram.js.map +1 -0
- package/package.json +67 -0
- package/src/data/custom-css-properties.json +200 -0
- package/src/js/cli.ts +98 -0
- package/src/js/cssToRules/mapDeclaration.ts +45 -0
- package/src/js/cssToRules/mapRule.ts +98 -0
- package/src/js/cssToRules/mapSelector.ts +78 -0
- package/src/js/cssToRules/mapSelectorPart.ts +161 -0
- package/src/js/cssToRules/mapValue.ts +84 -0
- package/src/js/cssToRules.ts +49 -0
- package/src/js/helpers/Replacer.ts +50 -0
- package/src/js/helpers/compileDeclarationBlock.ts +60 -0
- package/src/js/helpers/createModulesMapFromDependencies.ts +40 -0
- package/src/js/helpers/ensureObject.ts +17 -0
- package/src/js/helpers/pathToExpression.ts +5 -0
- package/src/js/helpers/uniqueSerialized.ts +7 -0
- package/src/js/index.ts +28 -0
- package/src/js/rulesToTree.ts +83 -0
- package/src/js/template.source.js +56 -0
- package/src/js/template.ts +50 -0
- package/src/js/treeToProgram.ts +220 -0
|
@@ -0,0 +1,84 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import deepExtend from "@mapsight/lib-js/object/deep-extend";
|
|
2
|
+
|
|
3
|
+
import type {Rules} from "./cssToRules.ts";
|
|
4
|
+
import type {DeclarationNode} from "./cssToRules/mapDeclaration.ts";
|
|
5
|
+
import type {Check} from "./cssToRules/mapSelectorPart.ts";
|
|
6
|
+
|
|
7
|
+
export type TreeLeaf = {
|
|
8
|
+
declarations?: DeclarationNode;
|
|
9
|
+
children: Child[];
|
|
10
|
+
stylePropExpressions?: Array<string>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type Child = {
|
|
14
|
+
conditions: Array<Array<Check>>;
|
|
15
|
+
declarations?: DeclarationNode;
|
|
16
|
+
stylePropExpressions?: Array<string>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type Tree = {
|
|
20
|
+
[style: string]: {
|
|
21
|
+
[state: string]: TreeLeaf;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default function rulesToTree(rules: Rules["rules"]) {
|
|
26
|
+
const tree: Tree = {};
|
|
27
|
+
|
|
28
|
+
rules.forEach((rule) => {
|
|
29
|
+
const ruleTree: Record<
|
|
30
|
+
string,
|
|
31
|
+
Record<string, Array<Array<Check>>>
|
|
32
|
+
> = {};
|
|
33
|
+
|
|
34
|
+
rule.conditions.forEach((condition) => {
|
|
35
|
+
const style = condition.style || "default";
|
|
36
|
+
const state = condition.state || "default";
|
|
37
|
+
|
|
38
|
+
ruleTree[style] ??= {};
|
|
39
|
+
ruleTree[style][state] ??= [];
|
|
40
|
+
|
|
41
|
+
if (condition.checks) {
|
|
42
|
+
ruleTree[style][state].push(condition.checks);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
Object.keys(ruleTree).forEach((style) => {
|
|
47
|
+
const sub = ruleTree[style];
|
|
48
|
+
if (!sub) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const styleTree = tree[style] || {};
|
|
53
|
+
Object.keys(sub).forEach((state) => {
|
|
54
|
+
const stateTree: Tree[string][string] = styleTree[state] ?? {
|
|
55
|
+
children: [],
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const conditions = sub[state];
|
|
59
|
+
if (conditions?.length) {
|
|
60
|
+
stateTree.children.push({
|
|
61
|
+
conditions: conditions,
|
|
62
|
+
declarations: rule.declarations,
|
|
63
|
+
stylePropExpressions: rule.__meta.stylePropExpressions,
|
|
64
|
+
});
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
stateTree.stylePropExpressions = (
|
|
68
|
+
stateTree.stylePropExpressions || []
|
|
69
|
+
).concat(rule.__meta.stylePropExpressions);
|
|
70
|
+
|
|
71
|
+
stateTree.declarations = deepExtend(
|
|
72
|
+
stateTree.declarations || {},
|
|
73
|
+
rule.declarations,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
styleTree[state] = stateTree;
|
|
77
|
+
});
|
|
78
|
+
tree[style] = styleTree;
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return tree;
|
|
83
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import get from "@mapsight/lib-js/object/getPath";
|
|
2
|
+
import replace from "@mapsight/lib-js/string/replaceRegex";
|
|
3
|
+
import createCachedStyleFunction from "@mapsight/lib-ol/style/createCachedStyleFunction";
|
|
4
|
+
/* styleImports-start: */
|
|
5
|
+
import __vectorStyle_circle from "ol/style/Circle";
|
|
6
|
+
|
|
7
|
+
/* :styleImports-end */
|
|
8
|
+
|
|
9
|
+
/** @type {(value: unknown) => string} */
|
|
10
|
+
const createHash = /* @__PURE__ */ (value) =>
|
|
11
|
+
/* @__PURE__ */ JSON.stringify(value);
|
|
12
|
+
/** @type {import('@mapsight/lib-ol/style/createCachedStyleFunction').StyleFunctionOptions} */
|
|
13
|
+
const styleOptions = {
|
|
14
|
+
constructorsMap: /* constructorMap-start: */ {
|
|
15
|
+
circle: __vectorStyle_circle,
|
|
16
|
+
} /* :constructorMap-end */,
|
|
17
|
+
allowedStyles: /* styleNames-start: */ [] /* :styleNames-end */,
|
|
18
|
+
allowedProps: /* styleProps-start: */ ["test"] /* :styleProps-end */,
|
|
19
|
+
declarationHashFunction: (env, props, hashPrefix, geometryType, style) => {
|
|
20
|
+
/** @type {Array<string|number>} */
|
|
21
|
+
const hash = [hashPrefix, geometryType];
|
|
22
|
+
// noinspection UnnecessaryLocalVariableJS
|
|
23
|
+
/** @type {(a: string|number) => void} */
|
|
24
|
+
const h = (a) => {
|
|
25
|
+
hash.push(a);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/* program1-start: */
|
|
29
|
+
h(1);
|
|
30
|
+
h("@" + createHash(style));
|
|
31
|
+
h("@" + createHash(props["test"]));
|
|
32
|
+
h("@" + createHash(env["test"]));
|
|
33
|
+
h("@" + createHash(get(props, ["path", "to", "test"])));
|
|
34
|
+
/* :program1-end */
|
|
35
|
+
|
|
36
|
+
return hash.join("|");
|
|
37
|
+
},
|
|
38
|
+
declarationFunction: (env, props, geometryType, style) => {
|
|
39
|
+
/** @type {Record<string, unknown>} */
|
|
40
|
+
const d = {};
|
|
41
|
+
|
|
42
|
+
/* program2-start: */
|
|
43
|
+
d.zIndex = 1;
|
|
44
|
+
d.calcTest = {value: "" + replace("a", "e", "hallo") + ""};
|
|
45
|
+
d.a = env.a;
|
|
46
|
+
d.b = props.b;
|
|
47
|
+
d.geometryType = geometryType;
|
|
48
|
+
d.style = style;
|
|
49
|
+
d.test = "foo";
|
|
50
|
+
/* :program2-end */
|
|
51
|
+
|
|
52
|
+
return d;
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export default createCachedStyleFunction(styleOptions);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {readFileSync} from "node:fs";
|
|
2
|
+
import {resolve} from "node:path";
|
|
3
|
+
|
|
4
|
+
import createModulesMapFromDependencies from "./helpers/createModulesMapFromDependencies.ts";
|
|
5
|
+
|
|
6
|
+
export type TemplateArgs<
|
|
7
|
+
TAdditionalData extends Record<string, unknown> = Record<string, unknown>,
|
|
8
|
+
> = TAdditionalData & {
|
|
9
|
+
__meta: {
|
|
10
|
+
styleNames: string[];
|
|
11
|
+
stateNames: string[];
|
|
12
|
+
groupNames: string[];
|
|
13
|
+
declarationNames: string[];
|
|
14
|
+
styleProps: string[];
|
|
15
|
+
stylePropExpressions: string[];
|
|
16
|
+
};
|
|
17
|
+
program1: string;
|
|
18
|
+
program2: string;
|
|
19
|
+
};
|
|
20
|
+
export type Template<
|
|
21
|
+
TAdditionalData extends Record<string, unknown> = Record<string, unknown>,
|
|
22
|
+
> = (args: TemplateArgs<TAdditionalData>) => string;
|
|
23
|
+
|
|
24
|
+
const templateSource = readFileSync(
|
|
25
|
+
resolve(import.meta.dirname, "template.source.js"),
|
|
26
|
+
"utf8",
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const replaceTag = (content: string, tag: string, value: string) => {
|
|
30
|
+
const pattern = `\\/\\*\\s*${tag}-start:\\s*\\*\\/[\\s\\S]*?\\/\\*\\s*:${tag}-end\\s*\\*\\/`;
|
|
31
|
+
return content.replace(new RegExp(pattern, "g"), () => value);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const defaultTemplate = ({__meta, program1, program2}: TemplateArgs) => {
|
|
35
|
+
const {declarationNames, styleProps, styleNames} = __meta;
|
|
36
|
+
const {map: constructorMap, imports: styleImports} =
|
|
37
|
+
createModulesMapFromDependencies(declarationNames);
|
|
38
|
+
|
|
39
|
+
let result = "// @ts-nocheck\n" + templateSource;
|
|
40
|
+
result = replaceTag(result, "styleImports", styleImports);
|
|
41
|
+
result = replaceTag(result, "constructorMap", constructorMap);
|
|
42
|
+
result = replaceTag(result, "styleNames", JSON.stringify(styleNames));
|
|
43
|
+
result = replaceTag(result, "styleProps", JSON.stringify(styleProps));
|
|
44
|
+
result = replaceTag(result, "program1", program1);
|
|
45
|
+
result = replaceTag(result, "program2", program2);
|
|
46
|
+
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default defaultTemplate;
|