Use ESM in Node.js code generation scripts (#2739)

This commit is contained in:
Bart Louwers
2024-08-23 14:31:36 +02:00
committed by GitHub
parent 8e2c84243a
commit 8ad4a51f2f
22 changed files with 241 additions and 579 deletions

View File

@@ -2,4 +2,4 @@
# Input hashes for repository rule npm_translate_lock(name = "npm", pnpm_lock = "//:pnpm-lock.yaml"). # Input hashes for repository rule npm_translate_lock(name = "npm", pnpm_lock = "//:pnpm-lock.yaml").
# This file should be checked into version control along with the pnpm-lock.yaml file. # This file should be checked into version control along with the pnpm-lock.yaml file.
pnpm-lock.yaml=172204574 pnpm-lock.yaml=172204574
package.json=-1427231250 package.json=794269200

View File

@@ -1,3 +1,4 @@
bazel-bin bazel-bin
bazel-out bazel-out
bazel-testlogs bazel-testlogs
node_modules

View File

@@ -208,6 +208,10 @@ jobs:
${{ runner.os }}-bazel- ${{ runner.os }}-bazel-
path: ~/.cache/bazel path: ~/.cache/bazel
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- name: Start server - name: Start server
run: | run: |
npm install npm install

2
.nvmrc
View File

@@ -1 +1 @@
20.10 20.14.0

View File

@@ -214,7 +214,7 @@ js_binary(
":style-code", ":style-code",
":style-spec", ":style-spec",
], ],
entry_point = "scripts/generate-style-code.js", entry_point = "scripts/generate-style-code.mjs",
) )
js_binary( js_binary(
@@ -222,14 +222,14 @@ js_binary(
data = [ data = [
":node_modules/argparse", ":node_modules/argparse",
], ],
entry_point = "shaders/generate_shader_code.js", entry_point = "shaders/generate_shader_code.mjs",
) )
js_library( js_library(
name = "style-spec", name = "style-spec",
srcs = ["scripts/style-spec.js"], srcs = ["scripts/style-spec.mjs"],
data = glob([ data = glob([
"scripts/style-spec-reference/*.js", "scripts/style-spec-reference/*.mjs",
"scripts/style-spec-reference/*.json", "scripts/style-spec-reference/*.json",
]), ]),
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
@@ -237,7 +237,7 @@ js_library(
js_library( js_library(
name = "style-code", name = "style-code",
srcs = ["scripts/style-code.js"], srcs = ["scripts/style-code.mjs"],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
deps = [ deps = [
":node_modules/ejs", ":node_modules/ejs",

View File

@@ -1,23 +1,30 @@
module(name = "maplibre") module(name = "maplibre")
bazel_dep(name = "apple_support", version = "1.16.0", repo_name = "build_bazel_apple_support") bazel_dep(name = "apple_support", version = "1.16.0", repo_name = "build_bazel_apple_support")
bazel_dep(name = "aspect_rules_js", version = "1.42.3")
bazel_dep(name = "bazel_skylib", version = "1.7.1") bazel_dep(name = "bazel_skylib", version = "1.7.1")
bazel_dep(name = "platforms", version = "0.0.10") bazel_dep(name = "platforms", version = "0.0.10")
bazel_dep(name = "rules_apple", version = "3.7.0", repo_name = "build_bazel_rules_apple") bazel_dep(name = "rules_apple", version = "3.7.0", repo_name = "build_bazel_rules_apple")
bazel_dep(name = "rules_swift", version = "2.1.1", repo_name = "build_bazel_rules_swift") bazel_dep(name = "rules_swift", version = "2.1.1", repo_name = "build_bazel_rules_swift")
bazel_dep(name = "rules_xcodeproj", version = "2.5.2") bazel_dep(name = "rules_xcodeproj", version = "2.5.2")
bazel_dep(name = "aspect_rules_js", version = "2.0.0")
bazel_dep(name = "rules_nodejs", version = "6.2.0")
npm = use_extension("@aspect_rules_js//npm:extensions.bzl", "npm") node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node", dev_dependency = True)
node.toolchain(node_version = "20.14.0")
npm = use_extension("@aspect_rules_js//npm:extensions.bzl", "npm", dev_dependency = True)
npm.npm_translate_lock( npm.npm_translate_lock(
name = "npm", name = "npm",
data = ["package.json"], data = ["package.json"],
pnpm_lock = "//:pnpm-lock.yaml", pnpm_lock = "//:pnpm-lock.yaml",
update_pnpm_lock = True, verify_node_modules_ignored = "//:.bazelignore",
) )
use_repo(npm, "npm") use_repo(npm, "npm")
pnpm = use_extension("@aspect_rules_js//npm:extensions.bzl", "pnpm") pnpm = use_extension("@aspect_rules_js//npm:extensions.bzl", "pnpm")
# Allows developers to use the matching pnpm version, for example:
# bazel run -- @pnpm --dir /home/runner/work/rules_js/rules_js install
use_repo(pnpm, "pnpm") use_repo(pnpm, "pnpm")
provisioning_profile_repository = use_extension("@build_bazel_rules_apple//apple:apple.bzl", "provisioning_profile_repository_extension") provisioning_profile_repository = use_extension("@build_bazel_rules_apple//apple:apple.bzl", "provisioning_profile_repository_extension")

43
package-lock.json generated
View File

@@ -20,9 +20,11 @@
"@mapbox/mvt-fixtures": "3.10.0", "@mapbox/mvt-fixtures": "3.10.0",
"@octokit/plugin-retry": "^7.1.0", "@octokit/plugin-retry": "^7.1.0",
"@octokit/rest": "^20.1.0", "@octokit/rest": "^20.1.0",
"@types/node": "^20.12.7", "@types/argparse": "^2.0.16",
"@types/ejs": "^3.1.5",
"@types/node": "^20.16.1",
"argparse": "^2.0.1", "argparse": "^2.0.1",
"csscolorparser": "~1.0.3", "csscolorparser": "^1.0.3",
"d3-queue": "3.0.7", "d3-queue": "3.0.7",
"diff": "5.2.0", "diff": "5.2.0",
"ejs": "^3.1.10", "ejs": "^3.1.10",
@@ -2127,13 +2129,28 @@
"node": ">=16.0.0" "node": ">=16.0.0"
} }
}, },
"node_modules/@types/node": { "node_modules/@types/argparse": {
"version": "20.12.7", "version": "2.0.16",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-2.0.16.tgz",
"integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", "integrity": "sha512-aMqBra2JlqpFeCWOinCtpRpiCkPIXH8hahW2+FkGzvWjfE5sAqtOcrjN5DRcMnTQqFDe6gb1CVYuGnBH0lhXwA==",
"dev": true, "dev": true,
"license": "MIT"
},
"node_modules/@types/ejs": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz",
"integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "20.16.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.1.tgz",
"integrity": "sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ==",
"dev": true,
"license": "MIT",
"dependencies": { "dependencies": {
"undici-types": "~5.26.4" "undici-types": "~6.19.2"
} }
}, },
"node_modules/@xmldom/xmldom": { "node_modules/@xmldom/xmldom": {
@@ -2670,7 +2687,8 @@
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz",
"integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==", "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==",
"dev": true "dev": true,
"license": "MIT"
}, },
"node_modules/d3-queue": { "node_modules/d3-queue": {
"version": "3.0.7", "version": "3.0.7",
@@ -5675,10 +5693,11 @@
} }
}, },
"node_modules/undici-types": { "node_modules/undici-types": {
"version": "5.26.5", "version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"dev": true "dev": true,
"license": "MIT"
}, },
"node_modules/universal-user-agent": { "node_modules/universal-user-agent": {
"version": "7.0.2", "version": "7.0.2",

View File

@@ -18,9 +18,11 @@
"@mapbox/mvt-fixtures": "3.10.0", "@mapbox/mvt-fixtures": "3.10.0",
"@octokit/plugin-retry": "^7.1.0", "@octokit/plugin-retry": "^7.1.0",
"@octokit/rest": "^20.1.0", "@octokit/rest": "^20.1.0",
"@types/node": "^20.12.7", "@types/argparse": "^2.0.16",
"@types/ejs": "^3.1.5",
"@types/node": "^20.16.1",
"argparse": "^2.0.1", "argparse": "^2.0.1",
"csscolorparser": "~1.0.3", "csscolorparser": "^1.0.3",
"d3-queue": "3.0.7", "d3-queue": "3.0.7",
"diff": "5.2.0", "diff": "5.2.0",
"ejs": "^3.1.10", "ejs": "^3.1.10",
@@ -42,5 +44,6 @@
"typescript": "^5.4.5", "typescript": "^5.4.5",
"xcode": "^3.0.1" "xcode": "^3.0.1"
}, },
"scripts": {} "scripts": {},
"type": "module"
} }

View File

@@ -1,458 +0,0 @@
#!/usr/bin/env node
'use strict';
const fs = require('fs');
const ejs = require('ejs');
const spec = require('../../../scripts/style-spec');
const _ = require('lodash');
// FIXME: https://github.com/mapbox/mapbox-gl-native/issues/15008
//delete spec.layout_circle["circle-sort-key"]
//delete spec.layout_line["line-sort-key"]
//delete spec.layout_fill["fill-sort-key"]
require('../../../scripts/style-code');
// Specification parsing //
const lightProperties = Object.keys(spec[`light`]).reduce((memo, name) => {
var property = spec[`light`][name];
property.name = name;
property['light-property'] = true;
property.doc = property.doc.replace(/°/g,'°');
memo.push(property);
return memo;
}, []);
// Collect layer types from spec
var layers = Object.keys(spec.layer.type.values).map((type) => {
const layoutProperties = Object.keys(spec[`layout_${type}`]).reduce((memo, name) => {
if (name !== 'visibility' && type !== 'location-indicator') {
spec[`layout_${type}`][name].name = name;
memo.push(spec[`layout_${type}`][name]);
}
return memo;
}, []);
const paintProperties = Object.keys(spec[`paint_${type}`]).reduce((memo, name) => {
if (type !== 'location-indicator') {
spec[`paint_${type}`][name].name = name;
memo.push(spec[`paint_${type}`][name]);
}
return memo;
}, []);
const locationLayoutProperties = Object.keys(spec[`layout_${type}`]).reduce((memo, name) => {
if (name !== 'visibility' && type === 'location-indicator') {
spec[`layout_${type}`][name].name = name;
memo.push(spec[`layout_${type}`][name]);
}
return memo;
}, []);
const locationPaintProperties = Object.keys(spec[`paint_${type}`]).reduce((memo, name) => {
if (type === 'location-indicator') {
spec[`paint_${type}`][name].name = name;
memo.push(spec[`paint_${type}`][name]);
}
return memo;
}, []);
return {
type: type,
doc: spec.layer.type.values[type].doc,
layoutProperties: layoutProperties,
paintProperties: paintProperties,
locationLayoutProperties: locationLayoutProperties,
locationPaintProperties: locationPaintProperties,
properties: layoutProperties.concat(paintProperties).concat(locationLayoutProperties).concat(locationPaintProperties)
};
});
// Process all layer properties
const uniqueArrayEnum = (prop, enums) => {
if (prop.value !== 'enum') return false;
const enumsEqual = (val1, val2) => val1.length === val1.length && val1.every((val, i) => val === val2[i]);
return enums.filter(e => enumsEqual(Object.keys(prop.values).sort(), Object.keys(e.values).sort())).length == 0;
};
const layoutProperties = _(layers).map('layoutProperties').flatten().value();
const paintProperties = _(layers).map('paintProperties').flatten().value();
const locationLayoutProperties = _(layers).map('locationLayoutProperties').flatten().value();
const locationPaintProperties = _(layers).map('locationPaintProperties').flatten().value();
const allProperties = _(layoutProperties).union(paintProperties).union(lightProperties).value();
let allEnumProperties = _(allProperties).filter({'type': 'enum'}).value();
const uniqueArrayEnumProperties = _(allProperties).filter({'type': 'array'}).filter(prop => uniqueArrayEnum(prop, allEnumProperties)).value();
const enumProperties = _(allEnumProperties).union(uniqueArrayEnumProperties).value();
global.propertyType = function propertyType(property) {
switch (property.type) {
case 'boolean':
return 'Boolean';
case 'number':
if (/bearing$/.test(property.name) &&
property.period == 360 &&
property.units =='degrees') {
return 'Double';
}
return /location$/.test(property.name) ? 'Double' : 'Float';
case 'formatted':
return 'Formatted';
case 'string':
case 'resolvedImage':
return 'String';
case 'enum':
return 'String';
case 'color':
return 'String';
case 'array':
return `${propertyType({type:property.value, name: property.name})}[]`;
default:
throw new Error(`unknown type for ${property.name}`);
}
}
global.propertyJavaType = function propertyType(property) {
switch (property.type) {
case 'boolean':
return 'boolean';
case 'number':
return 'float';
case 'formatted':
return 'Formatted';
case 'string':
case 'resolvedImage':
return 'String';
case 'enum':
return 'String';
case 'color':
return 'String';
case 'array':
return `${propertyJavaType({type:property.value})}[]`;
default:
throw new Error(`unknown type for ${property.name}`);
}
}
global.propertyJNIType = function propertyJNIType(property) {
switch (property.type) {
case 'boolean':
return 'jboolean';
case 'number':
return 'jfloat';
case 'String':
return 'String';
case 'enum':
return 'String';
case 'color':
return 'String';
case 'array':
return `jarray<${propertyType({type:property.value})}[]>`;
default:
return 'jobject*';
}
}
global.propertyNativeType = function (property) {
if (/-translate-anchor$/.test(property.name)) {
return 'TranslateAnchorType';
}
if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) {
return 'AlignmentType';
}
if (/^(text|icon)-anchor$/.test(property.name)) {
return 'SymbolAnchorType';
}
switch (property.type) {
case 'boolean':
return 'bool';
case 'number':
return 'float';
case 'formatted':
case 'string':
case 'resolvedImage': // TODO: replace once we implement image expressions
return 'std::string';
case 'enum':
if(property['light-property']){
return `Light${camelize(property.name)}Type`;
}
return `${camelize(property.name)}Type`;
case 'color':
return `Color`;
case 'array':
if (property.length) {
return `std::array<${propertyType({type: property.value})}, ${property.length}>`;
} else {
return `std::vector<${propertyType({type: property.value})}>`;
}
default: throw new Error(`unknown type for ${property.name}`)
}
}
global.propertyTypeAnnotation = function propertyTypeAnnotation(property) {
switch (property.type) {
case 'enum':
return `@Property.${snakeCaseUpper(property.name)}`;
default:
return "";
}
};
global.defaultExpressionJava = function(property) {
switch (property.type) {
case 'boolean':
return 'boolean';
case 'number':
return 'number';
case 'formatted':
return 'format';
case 'resolvedImage':
return "image";
case 'string':
case 'image':
return "string";
case 'enum':
return "string";
case 'color':
return 'toColor';
case 'array':
return "array";
default: return "string";
}
}
global.defaultValueJava = function(property) {
if(property.name.endsWith("-pattern")) {
return '"pedestrian-polygon"';
}
if(property.name.endsWith("-font")) {
return 'new String[]{"Open Sans Regular", "Arial Unicode MS Regular"}';
}
switch (property.type) {
case 'boolean':
return 'true';
case 'number':
if (/bearing$/.test(property.name) &&
property.period == 360 &&
property.units =='degrees') {
return '0.3';
}
return '0.3f';
case 'formatted':
return 'new Formatted(new FormattedSection("default"))'
case 'string':
case 'resolvedImage':
return '"' + property['default'] + '"';
case 'enum':
return snakeCaseUpper(property.name) + "_" + snakeCaseUpper(Object.keys(property.values)[0]);
case 'color':
return '"rgba(255,128,0,0.7)"';
case 'array':
switch (property.value) {
case 'string':
case 'enum':
if (property['default'] !== undefined) {
return '[' + property['default'] + ']';
} else {
return 'new String[0]';
}
case 'number':
var isDouble = /location$/.test(property.name)
console.log(isDouble)
var result = 'new ' + (isDouble ? 'Double' : 'Float') + '[] {';
for (var i = 0; i < property.length; i++) {
result += isDouble ? '0.0' : '0f';
if (i +1 != property.length) {
result += ', ';
}
}
return result + '}';
}
default: throw new Error(`unknown type for ${property.name}`)
}
}
/**
* Produces documentation for property factory methods
*/
global.propertyFactoryMethodDoc = function (property) {
var replaceIfPixels = function (doc) {
return doc.replace('pixels', 'density-independent pixels')
}
let doc = replaceIfPixels(property.doc);
// Match other items in back ticks
doc = doc.replace(/`(.+?)`/g, function (m, symbol, offset, str) {
if (str.substr(offset - 4, 3) !== 'CSS' && symbol[0].toUpperCase() != symbol[0] && _(enumProperties).filter({'name': symbol}).value().length > 0) {
// Property 'enums'
symbol = snakeCaseUpper(symbol);
return '{@link Property.' + symbol + '}';
} else if( _(allProperties).filter({'name': symbol}).value().length > 0) {
// Other properties
return '{@link PropertyFactory#' + camelizeWithLeadingLowercase(symbol) + '}';
} else {
// Left overs
return '`' + symbol + '`';
}
});
return doc;
};
/**
* Produces documentation for property value constants
*/
global.propertyValueDoc = function (property, value) {
// Match references to other property names & values.
// Requires the format 'When `foo` is set to `bar`,'.
let doc = property.values[value].doc.replace(/When `(.+?)` is set to `(.+?)`(?: or `([^`]+?)`)?,/g, function (m, peerPropertyName, propertyValue, secondPropertyValue, offset, str) {
let otherProperty = snakeCaseUpper(peerPropertyName);
let otherValue = snakeCaseUpper(peerPropertyName) + '_' + snakeCaseUpper(propertyValue);
const firstPropertyValue = 'When {@link ' + `${otherProperty}` + '} is set to {@link Property#' + `${otherValue}` + '}';
if (secondPropertyValue) {
return firstPropertyValue + ` or {@link Property#${snakeCaseUpper(peerPropertyName) + '_' + snakeCaseUpper(secondPropertyValue)}},`;
} else {
return firstPropertyValue + ',';
}
});
// Match references to our own property values.
// Requires the format 'is equivalent to `bar`'.
doc = doc.replace(/is equivalent to `(.+?)`/g, function(m, propertyValue, offset, str) {
propertyValue = snakeCaseUpper(property.name) + '_' + snakeCaseUpper(propertyValue);
return 'is equivalent to {@link Property#' + propertyValue + '}';
});
// Match other items in back ticks
doc = doc.replace(/`(.+?)`/g, function (m, symbol, offset, str) {
if ('values' in property && Object.keys(property.values).indexOf(symbol) !== -1) {
// Property values
propertyValue = snakeCaseUpper(property.name) + '_' + snakeCaseUpper(symbol);
console.log("Transforming", symbol, propertyValue);
return '{@link Property#' + `${propertyValue}` + '}';
} else if (str.substr(offset - 4, 3) !== 'CSS' && symbol[0].toUpperCase() != symbol[0]) {
// Property 'enums'
if (symbol === 'symbol-sort-key') {
return 'symbol sort key';
}
symbol = snakeCaseUpper(symbol);
return '{@link ' + symbol + '}';
} else {
// Left overs
return symbol
}
});
return doc;
};
global.isLightProperty = function (property) {
return property['light-property'] === true;
};
global.propertyValueType = function (property) {
switch (property['property-type']) {
default:
return `PropertyValue<${evaluatedType(property)}>`;
}
};
global.evaluatedType = function (property) {
if (/-translate-anchor$/.test(property.name)) {
return 'TranslateAnchorType';
}
if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) {
return 'AlignmentType';
}
if (/^(text|icon)-anchor$/.test(property.name)) {
return 'SymbolAnchorType';
}
if (/position/.test(property.name)) {
return 'Position';
}
switch (property.type) {
case 'boolean':
return 'bool';
case 'number':
return 'float';
case 'formatted':
case 'string':
case 'image':
return 'std::string';
case 'enum':
return (isLightProperty(property) ? 'Light' : '') + `${camelize(property.name)}Type`;
case 'color':
return `Color`;
case 'array':
if (property.length) {
return `std::array<${evaluatedType({type: property.value})}, ${property.length}>`;
} else {
return `std::vector<${evaluatedType({type: property.value})}>`;
}
default: throw new Error(`unknown type for ${property.name}`)
}
};
global.supportsZoomFunction = function (property) {
return property.expression && property.expression.parameters.indexOf('zoom') > -1;
};
global.supportsPropertyFunction = function (property) {
return property['property-type'] === 'data-driven' || property['property-type'] === 'cross-faded-data-driven';
};
// Template processing //
// Java + JNI Light (Peer model)
const lightJava = ejs.compile(fs.readFileSync('MapLibreAndroid/src/main/java/org/maplibre/android/style/light/light.java.ejs', 'utf8'), {strict: true});
const lightJavaUnitTests = ejs.compile(fs.readFileSync('MapLibreAndroidTestApp/src/androidTest/java/org/maplibre/android/testapp/style/light.junit.ejs', 'utf8'), {strict: true});
writeIfModified(`MapLibreAndroid/src/main/java/org/maplibre/android/style/light/Light.java`, lightJava({properties: lightProperties}));
writeIfModified(`MapLibreAndroidTestApp/src/androidTest/java/org/maplibre/android/testapp/style/LightTest.java`, lightJavaUnitTests({properties: lightProperties}));
// Java
const layerJava = ejs.compile(fs.readFileSync('MapLibreAndroid/src/main/java/org/maplibre/android/style/layers/layer.java.ejs', 'utf8'), {strict: true});
const layerJavaUnitTests = ejs.compile(fs.readFileSync('MapLibreAndroidTestApp/src/androidTest/java/org/maplibre/android/testapp/style/layer.junit.ejs', 'utf8'), {strict: true});
for (const layer of layers) {
var srcDir = 'MapLibreAndroid/src/main/java/org/maplibre/android/style/layers/'
var testDir = 'MapLibreAndroidTestApp/src/androidTest/java/org/maplibre/android/testapp/style/'
if (layer.type === 'location-indicator') {
srcDir = 'MapLibreAndroid/src/main/java/org/maplibre/android/location/'
testDir = 'MapLibreAndroidTestApp/src/androidTest/java/org/maplibre/android/location/'
}
writeIfModified(srcDir + `${camelize(layer.type)}Layer.java`, layerJava(layer));
writeIfModified(testDir + `${camelize(layer.type)}LayerTest.java`, layerJavaUnitTests(layer));
}
// Jni
const layerHpp = ejs.compile(fs.readFileSync('MapLibreAndroid/src/cpp/style/layers/layer.hpp.ejs', 'utf8'), {strict: true});
const layerCpp = ejs.compile(fs.readFileSync('MapLibreAndroid/src/cpp/style/layers/layer.cpp.ejs', 'utf8'), {strict: true});
for (const layer of layers) {
const layerFileName = layer.type.replace('-', '_');
writeIfModified(`MapLibreAndroid/src/cpp/style/layers/${layerFileName}_layer.hpp`, layerHpp(layer));
writeIfModified(`MapLibreAndroid/src/cpp/style/layers/${layerFileName}_layer.cpp`, layerCpp(layer));
}
// Java PropertyFactory
const propertyFactoryTemplate = ejs.compile(fs.readFileSync('MapLibreAndroid/src/main/java/org/maplibre/android/style/layers/property_factory.java.ejs', 'utf8'), {strict: true});
var propertyFactorySrcDir = 'MapLibreAndroid/src/main/java/org/maplibre/android/style/layers/PropertyFactory.java'
writeIfModified(
propertyFactorySrcDir,
propertyFactoryTemplate({layoutProperties: layoutProperties, paintProperties: paintProperties, locationIndicator: false})
);
var locationPropertyFactorySrcDir = 'MapLibreAndroid/src/main/java/org/maplibre/android/location/LocationPropertyFactory.java'
writeIfModified(
locationPropertyFactorySrcDir,
propertyFactoryTemplate({layoutProperties: locationLayoutProperties, paintProperties: locationPaintProperties, locationIndicator: true})
);
// Java Property
const enumPropertyJavaTemplate = ejs.compile(fs.readFileSync('MapLibreAndroid/src/main/java/org/maplibre/android/style/layers/property.java.ejs', 'utf8'), {strict: true});
writeIfModified(
`MapLibreAndroid/src/main/java/org/maplibre/android/style/layers/Property.java`,
enumPropertyJavaTemplate({properties: enumProperties})
);

View File

@@ -334,7 +334,7 @@ js_binary(
"//:style-code", "//:style-code",
"//:style-spec", "//:style-spec",
], ],
entry_point = "scripts/generate-style-code.js", entry_point = "scripts/generate-style-code.mjs",
) )
js_library( js_library(

View File

@@ -1,14 +1,27 @@
#!/usr/bin/env node import { ArgumentParser } from "argparse";
'use strict'; import path from "node:path";
import _ from "lodash";
import colorParser from "csscolorparser";
import assert from "assert";
const { ArgumentParser } = require("argparse"); import { readAndCompile, writeIfModified, camelize, unhyphenate } from "../../../scripts/style-code.mjs";
const fs = require('fs');
const path = require('path');
const _ = require('lodash');
const colorParser = require('csscolorparser');
const assert = require('assert');
require('../../../scripts/style-code'); import cocoaConventions from './style-spec-cocoa-conventions-v8.json' with { type: "json" };
import styleSpec from '../../../scripts/style-spec-reference/v8.json' with { type: "json" };
import styleSpecOverrides from './style-spec-overrides-v8.json' with { type: "json" };
function setupGlobalEjsHelpers() {
const funcs = {
camelize,
unhyphenate
};
for (const [funcName, func] of Object.entries(funcs)) {
// @ts-ignore
global[funcName] = func;
}
}
setupGlobalEjsHelpers();
// Parse command line // Parse command line
const args = (() => { const args = (() => {
@@ -22,11 +35,10 @@ const args = (() => {
return parser.parse_args(); return parser.parse_args();
})(); })();
const cocoaConventions = require('./style-spec-cocoa-conventions-v8.json');
const prefix = 'MLN'; const prefix = 'MLN';
const suffix = 'StyleLayer'; const suffix = 'StyleLayer';
let spec = _.merge(require('../../../scripts/style-spec-reference/v8'), require('./style-spec-overrides-v8.json')); let spec = _.merge(styleSpec, styleSpecOverrides);
class ConventionOverride { class ConventionOverride {
constructor(val) { constructor(val) {
@@ -787,7 +799,7 @@ const lightProperties = Object.keys(spec['light']).reduce((memo, name) => {
const lightDoc = spec['light-cocoa-doc']; const lightDoc = spec['light-cocoa-doc'];
const lightType = 'light'; const lightType = 'light';
const root = path.dirname(path.dirname(path.dirname(__dirname))); const root = path.dirname(path.dirname(path.dirname(import.meta.dirname)));
const outLocation = args.out ? args.out : root; const outLocation = args.out ? args.out : root;
const layerH = readAndCompile('platform/darwin/src/MLNStyleLayer.h.ejs', root); const layerH = readAndCompile('platform/darwin/src/MLNStyleLayer.h.ejs', root);

View File

@@ -1 +0,0 @@
module.exports = require('../../..//scripts/style-spec-reference/v8');

View File

@@ -1,11 +1,7 @@
#!/usr/bin/env node import fs from "node:fs";
import path from "node:path";
'use strict'; import { execFileSync } from "node:child_process";
import _ from "lodash";
const fs = require('fs');
const path = require('path');
const execFileSync = require('child_process').execFileSync;
const _ = require('lodash');
const keyword = /\bMLN_EXPORT\b/; const keyword = /\bMLN_EXPORT\b/;

View File

@@ -1,13 +1,9 @@
#!/usr/bin/env node import { ArgumentParser } from "argparse";
'use strict'; import * as path from "node:path";
import spec from "./style-spec.mjs";
import colorParser from "csscolorparser";
const { ArgumentParser } = require("argparse"); import { camelize, camelizeWithLeadingLowercase, readAndCompile, writeIfModified } from "./style-code.mjs";
const path = require('path');
const fs = require('fs');
const spec = require('./style-spec');
const colorParser = require('csscolorparser');
require('./style-code');
// Parse command line // Parse command line
const args = (() => { const args = (() => {
@@ -21,22 +17,33 @@ const args = (() => {
return parser.parse_args(); return parser.parse_args();
})(); })();
function parseCSSColor(str) { function parseCSSColor(/** @type {string} **/ str) {
const color = colorParser.parseCSSColor(str); const color = colorParser.parseCSSColor(str);
if (!color) throw new Error("Could not parse CSS color");
return [ return [
color[0] / 255 * color[3], color[1] / 255 * color[3], color[2] / 255 * color[3], color[3] color[0] / 255 * color[3], color[1] / 255 * color[3], color[2] / 255 * color[3], color[3]
]; ];
} }
global.isLightProperty = function (property) { /**
* @param {any} property
*/
function isLightProperty(property) {
return property['light-property'] === true; return property['light-property'] === true;
}; };
global.isOverridable = function (property) { /**
* @param {any} property
*/
function isOverridable(property) {
return ['text-color'].includes(property.name); return ['text-color'].includes(property.name);
}; };
global.expressionType = function (property) { /**
* @param {any} property
* @returns {string}
*/
function expressionType(property) {
switch (property.type) { switch (property.type) {
case 'boolean': case 'boolean':
return 'BooleanType'; return 'BooleanType';
@@ -57,7 +64,12 @@ global.expressionType = function (property) {
} }
}; };
global.evaluatedType = function (property) { /**
*
* @param {any} property
* @returns {string}
*/
function evaluatedType(property) {
if (/-translate-anchor$/.test(property.name)) { if (/-translate-anchor$/.test(property.name)) {
return 'TranslateAnchorType'; return 'TranslateAnchorType';
} }
@@ -102,7 +114,12 @@ global.evaluatedType = function (property) {
} }
}; };
/**
* @param {any} property
* @param {any} type
*/
function attributeUniformType(property, type) { function attributeUniformType(property, type) {
/** @type {Record<string, string[]>} **/
const attributeNameExceptions = { const attributeNameExceptions = {
'text-opacity': ['opacity'], 'text-opacity': ['opacity'],
'icon-opacity': ['opacity'], 'icon-opacity': ['opacity'],
@@ -120,6 +137,7 @@ function attributeUniformType(property, type) {
'fill-pattern': ['pattern_to', 'pattern_from'], 'fill-pattern': ['pattern_to', 'pattern_from'],
'fill-extrusion-pattern': ['pattern_to', 'pattern_from'] 'fill-extrusion-pattern': ['pattern_to', 'pattern_from']
} }
/** @type {string[]} **/
const names = attributeNameExceptions[property.name] || const names = attributeNameExceptions[property.name] ||
[ property.name.replace(type + '-', '').replace(/-/g, '_') ]; [ property.name.replace(type + '-', '').replace(/-/g, '_') ];
@@ -128,7 +146,10 @@ function attributeUniformType(property, type) {
}).join(', '); }).join(', ');
} }
global.layoutPropertyType = function (property) { /**
* @param {any} property
*/
function layoutPropertyType(property) {
switch (property['property-type']) { switch (property['property-type']) {
case 'data-driven': case 'data-driven':
case 'cross-faded-data-driven': case 'cross-faded-data-driven':
@@ -138,7 +159,13 @@ global.layoutPropertyType = function (property) {
} }
}; };
global.paintPropertyType = function (property, type) { /**
*
* @param {any} property
* @param {any} type
* @returns
*/
function paintPropertyType(property, type) {
switch (property['property-type']) { switch (property['property-type']) {
case 'data-driven': case 'data-driven':
if (isOverridable(property)) if (isOverridable(property))
@@ -153,7 +180,10 @@ global.paintPropertyType = function (property, type) {
} }
}; };
global.propertyValueType = function (property) { /**
* @param {any} property
*/
function propertyValueType(property) {
switch (property['property-type']) { switch (property['property-type']) {
case 'color-ramp': case 'color-ramp':
return `ColorRampPropertyValue`; return `ColorRampPropertyValue`;
@@ -162,6 +192,11 @@ global.propertyValueType = function (property) {
} }
}; };
/**
* @param {any} property
* @param {number} num
* @returns {string}
*/
function formatNumber(property, num = 0) { function formatNumber(property, num = 0) {
if (evaluatedType(property) === "float") { if (evaluatedType(property) === "float") {
const str = num.toString(); const str = num.toString();
@@ -170,7 +205,10 @@ function formatNumber(property, num = 0) {
return num.toString(); return num.toString();
} }
global.defaultValue = function (property) { /**
* @param {any} property
*/
function defaultValue(property) {
// https://github.com/mapbox/mapbox-gl-native/issues/5258 // https://github.com/mapbox/mapbox-gl-native/issues/5258
if (property.name === 'line-round-limit') { if (property.name === 'line-round-limit') {
return 1; return 1;
@@ -210,7 +248,7 @@ global.defaultValue = function (property) {
return `{ ${color} }`; return `{ ${color} }`;
} }
case 'array': case 'array':
const defaults = (property.default || []).map((e) => defaultValue({ type: property.value, default: e })); const defaults = (property.default || []).map((/** @type {any} **/ e) => defaultValue({ type: property.value, default: e }));
if (property.length) { if (property.length) {
return `{{${defaults.join(', ')}}}`; return `{{${defaults.join(', ')}}}`;
} else { } else {
@@ -222,7 +260,7 @@ global.defaultValue = function (property) {
}; };
console.log("Generating style code..."); console.log("Generating style code...");
const root = path.dirname(__dirname); const root = path.join(import.meta.dirname, "..")
const outLocation = args.out ? args.out : root; const outLocation = args.out ? args.out : root;
const layerHpp = readAndCompile(`include/mbgl/style/layers/layer.hpp.ejs`, root); const layerHpp = readAndCompile(`include/mbgl/style/layers/layer.hpp.ejs`, root);
@@ -241,7 +279,9 @@ spec.paint_line['line-floor-width'] = {
}; };
const layers = Object.keys(spec.layer.type.values).map((type) => { const layers = Object.keys(spec.layer.type.values).map((type) => {
const layoutProperties = Object.keys(spec[`layout_${type}`]).reduce((memo, name) => {
/** @type {any[]} */
const layoutProperties = Object.keys(spec[`layout_${type}`]).reduce((/** @type {any} **/ memo, name) => {
if (name !== 'visibility') { if (name !== 'visibility') {
spec[`layout_${type}`][name].name = name; spec[`layout_${type}`][name].name = name;
memo.push(spec[`layout_${type}`][name]); memo.push(spec[`layout_${type}`][name]);
@@ -253,7 +293,8 @@ const layers = Object.keys(spec.layer.type.values).map((type) => {
// to get a deterministic order. // to get a deterministic order.
layoutProperties.sort((a, b) => collator.compare(a.name, b.name)); layoutProperties.sort((a, b) => collator.compare(a.name, b.name));
const paintProperties = Object.keys(spec[`paint_${type}`]).reduce((memo, name) => { /** @type {any[]} */
const paintProperties = Object.keys(spec[`paint_${type}`]).reduce((/** @type {any} **/ memo, name) => {
spec[`paint_${type}`][name].name = name; spec[`paint_${type}`][name].name = name;
memo.push(spec[`paint_${type}`][name]); memo.push(spec[`paint_${type}`][name]);
return memo; return memo;
@@ -273,7 +314,27 @@ const layers = Object.keys(spec.layer.type.values).map((type) => {
}; };
}); });
for (const layer of layers) { function setupGlobalEjsHelpers() {
const funcs = {
layoutPropertyType,
evaluatedType,
isOverridable,
expressionType,
defaultValue,
paintPropertyType,
propertyValueType,
camelize,
camelizeWithLeadingLowercase
};
for (const [funcName, func] of Object.entries(funcs)) {
// @ts-ignore
global[funcName] = func;
}
}
setupGlobalEjsHelpers();
for (let layer of layers) {
const layerFileName = layer.type.replace('-', '_'); const layerFileName = layer.type.replace('-', '_');
writeIfModified(`src/mbgl/style/layers/${layerFileName}_layer_properties.hpp`, propertiesHpp(layer), outLocation); writeIfModified(`src/mbgl/style/layers/${layerFileName}_layer_properties.hpp`, propertiesHpp(layer), outLocation);
@@ -289,7 +350,8 @@ for (const layer of layers) {
} }
// Light // Light
const lightProperties = Object.keys(spec[`light`]).reduce((memo, name) => { /** @type {any[]} **/
const lightProperties = Object.keys(spec[`light`]).reduce((/** @type {any} **/ memo, name) => {
var property = spec[`light`][name]; var property = spec[`light`][name];
property.name = name; property.name = name;
property['light-property'] = true; property['light-property'] = true;

1
scripts/global.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
declare module "csscolorparser"

View File

@@ -3,9 +3,16 @@
"strict": true, "strict": true,
"allowJs": true, "allowJs": true,
"checkJs": true, "checkJs": true,
"module": "NodeNext" "module": "NodeNext",
"skipLibCheck": true,
"resolveJsonModule": true,
"maxNodeModuleJsDepth": 0
}, },
"include": [ "include": [
"**/*.mjs" "./**/*.mjs",
"**/*.d.ts"
],
"exclude": [
"node_modules"
] ]
} }

View File

@@ -1,42 +1,63 @@
// Global functions // // EJS Helpers
const ejs = require('ejs'); import ejs from "ejs";
const fs = require('fs'); import fs from "node:fs";
const path = require('path');
global.iff = function (condition, val) { import path from "node:path";
/**
* @param {() => boolean} condition
* @param {string} val
* @returns {string}
*/
export function iff(condition, val) {
return condition() ? val : ""; return condition() ? val : "";
}; };
global.camelize = function (str) { /**
* @param {string} str
* @returns {string}
*/
export function camelize(str) {
return str.replace(/(?:^|-)(.)/g, function (_, x) { return str.replace(/(?:^|-)(.)/g, function (_, x) {
return x.toUpperCase(); return x.toUpperCase();
}); });
}; };
global.camelizeWithLeadingLowercase = function (str) { /**
* @param {string} str
* @returns {string}
*/
export function camelizeWithLeadingLowercase(str) {
return str.replace(/-(.)/g, function (_, x) { return str.replace(/-(.)/g, function (_, x) {
return x.toUpperCase(); return x.toUpperCase();
}); });
}; };
global.snakeCaseUpper = function snakeCaseUpper(str) { export function snakeCaseUpper(/** @type {string} **/ str) {
return str.replace(/-/g, "_").toUpperCase(); return str.replace(/-/g, "_").toUpperCase();
}; };
global.unhyphenate = function (str) { export function unhyphenate (/** @type {string} **/ str) {
return str.replace(/-/g, " "); return str.replace(/-/g, " ");
}; };
// Write out a list of files that this script is modifying so that we can check // Write out a list of files that this script is modifying so that we can check
var files = []; /** @type {string[]} **/
let files = [];
process.on('exit', function() { process.on('exit', function() {
const list = path.join(path.dirname(process.argv[1]), path.basename(process.argv[1], '.js') + '.list'); const list = path.join(path.dirname(process.argv[1]), path.basename(process.argv[1], '.js') + '.list');
console.log(`Writing files that this script modifies to: ${list}`); console.log(`Writing files that this script modifies to: ${list}`);
fs.writeFileSync(list, files.join("\n")); fs.writeFileSync(list, files.join("\n"));
}); });
global.writeIfModified = function(filename, newContent, output) { /**
*
* @param {string} filename
* @param {string} newContent
* @param {string} output
*/
export function writeIfModified(filename, newContent, output) {
if (output) { if (output) {
filename = path.resolve(path.join(output, filename)); filename = path.resolve(path.join(output, filename));
} }
@@ -61,7 +82,13 @@ global.writeIfModified = function(filename, newContent, output) {
console.warn(`* Updating outdated file '${filename}'`); console.warn(`* Updating outdated file '${filename}'`);
}; };
global.readAndCompile = function(filename, root) { /**
*
* @param {string} filename
* @param {string} root
* @returns
*/
export function readAndCompile(filename, root) {
if (root) { if (root) {
filename = path.resolve(path.join(root, filename)); filename = path.resolve(path.join(root, filename));
} }

View File

@@ -1,7 +1,10 @@
const referenceSpec = require('../scripts/style-spec-reference/v8'); import referenceSpec from "../scripts/style-spec-reference/v8.json" with { type: "json" };
referenceSpec.layer.type.values["location-indicator"] = {}; /** @type {any} */
referenceSpec["layout_location-indicator"] = { let modifiedReferenceSpec = referenceSpec;
modifiedReferenceSpec.layer.type.values["location-indicator"] = {};
modifiedReferenceSpec["layout_location-indicator"] = {
"top-image": { "top-image": {
"type": "resolvedImage", "type": "resolvedImage",
"property-type": "data-constant", "property-type": "data-constant",
@@ -37,7 +40,7 @@ referenceSpec["layout_location-indicator"] = {
} }
}; };
referenceSpec["paint_location-indicator"] = { modifiedReferenceSpec["paint_location-indicator"] = {
"perspective-compensation": { "perspective-compensation": {
"type": "number", "type": "number",
"default": "0.85", "default": "0.85",
@@ -65,7 +68,6 @@ referenceSpec["paint_location-indicator"] = {
}, },
"bearing": { "bearing": {
"type": "number", "type": "number",
"default": "0",
"default": 0, "default": 0,
"period": 360, "period": 360,
"units": "degrees", "units": "degrees",
@@ -176,21 +178,7 @@ referenceSpec["paint_location-indicator"] = {
}, },
"transition": true, "transition": true,
"doc": "The color for drawing the accuracy radius border. To adjust transparency, set the alpha component of the color accordingly." "doc": "The color for drawing the accuracy radius border. To adjust transparency, set the alpha component of the color accordingly."
},
"bearing": {
"type": "number",
"default": "0",
"default": 0,
"period": 360,
"units": "degrees",
"property-type": "data-constant",
"expression": {
"interpolated": false,
"parameters": [ ]
},
"transition": false,
"doc": "The bearing of the location indicator."
} }
}; };
var spec = module.exports = referenceSpec export default modifiedReferenceSpec;

View File

@@ -1,12 +1,8 @@
#!/usr/bin/env node
'use strict';
console.log("Generating shaders..."); console.log("Generating shaders...");
const { ArgumentParser } = require("argparse"); import { ArgumentParser } from "argparse";
const path = require("node:path"); import path from "node:path";
const fs = require("node:fs") import fs from "node:fs";
const os = require("node:os");
const generatedHeader = `// Generated code, do not modify this file!`; const generatedHeader = `// Generated code, do not modify this file!`;
@@ -220,7 +216,7 @@ const args = (() => {
// Generate shader source headers // Generate shader source headers
const root = path.dirname(__dirname); const root = path.dirname(import.meta.dirname);
const outLocation = args.out ? args.out : root; const outLocation = args.out ? args.out : root;
const shaderRoot = path.join(root, "shaders"); const shaderRoot = path.join(root, "shaders");
const outputRoot = path.join(outLocation, "include/mbgl/shaders"); const outputRoot = path.join(outLocation, "include/mbgl/shaders");

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env node import express from "express";
/* jshint node: true */ import path from "node:path";
'use strict';
if (!import.meta.dirname) throw new Error("Could not get import.meta.dirname. Use Node.js 20.11 or newer.");
// This needs to be here to make sure the pipe stays open. // This needs to be here to make sure the pipe stays open.
// We're waiting until the stdin pipe gets closed (e.g. because the parent // We're waiting until the stdin pipe gets closed (e.g. because the parent
@@ -8,9 +9,6 @@
process.stdin.on('readable', function() {}); process.stdin.on('readable', function() {});
process.stdin.on('end', function() { process.exit(0); }); process.stdin.on('end', function() { process.exit(0); });
var fs = require('fs');
var express = require('express');
var app = express(); var app = express();
// We're manually setting Etag headers. // We're manually setting Etag headers.
@@ -170,7 +168,7 @@ app.get('/load/:number(\\d+)', function(req, res) {
}); });
app.get('/online/:style(*)', function(req, res) { app.get('/online/:style(*)', function(req, res) {
const file = (__dirname + "/../fixtures/map/online/" + req.params.style).replace("storage/../", ""); const file = path.join(import.meta.dirname, "../fixtures/map/online", req.params.style);
res.sendFile(file); // Set disposition and send it. res.sendFile(file); // Set disposition and send it.
// res.status(200).send(); // res.status(200).send();
// res.send('Request ' + req.params.style); // res.send('Request ' + req.params.style);