diff --git a/.aspect/rules/external_repository_action_cache/npm_translate_lock_LTE4Nzc1MDcwNjU= b/.aspect/rules/external_repository_action_cache/npm_translate_lock_LTE4Nzc1MDcwNjU= index ab3b90a2923..ef0cb5d4645 100755 --- a/.aspect/rules/external_repository_action_cache/npm_translate_lock_LTE4Nzc1MDcwNjU= +++ b/.aspect/rules/external_repository_action_cache/npm_translate_lock_LTE4Nzc1MDcwNjU= @@ -2,4 +2,4 @@ # 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. pnpm-lock.yaml=172204574 -package.json=-1427231250 +package.json=794269200 diff --git a/.bazelignore b/.bazelignore index 46c2539b481..7b4ec7aef3d 100644 --- a/.bazelignore +++ b/.bazelignore @@ -1,3 +1,4 @@ bazel-bin bazel-out bazel-testlogs +node_modules \ No newline at end of file diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml index 4bf224f1f15..c0b873383f7 100644 --- a/.github/workflows/linux-ci.yml +++ b/.github/workflows/linux-ci.yml @@ -208,6 +208,10 @@ jobs: ${{ runner.os }}-bazel- path: ~/.cache/bazel + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + - name: Start server run: | npm install diff --git a/.nvmrc b/.nvmrc index 89e0c3dba3b..805efa9f6fa 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.10 \ No newline at end of file +20.14.0 \ No newline at end of file diff --git a/BUILD.bazel b/BUILD.bazel index ec5888a557b..33e2d795f2f 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -214,7 +214,7 @@ js_binary( ":style-code", ":style-spec", ], - entry_point = "scripts/generate-style-code.js", + entry_point = "scripts/generate-style-code.mjs", ) js_binary( @@ -222,14 +222,14 @@ js_binary( data = [ ":node_modules/argparse", ], - entry_point = "shaders/generate_shader_code.js", + entry_point = "shaders/generate_shader_code.mjs", ) js_library( name = "style-spec", - srcs = ["scripts/style-spec.js"], + srcs = ["scripts/style-spec.mjs"], data = glob([ - "scripts/style-spec-reference/*.js", + "scripts/style-spec-reference/*.mjs", "scripts/style-spec-reference/*.json", ]), visibility = ["//visibility:public"], @@ -237,7 +237,7 @@ js_library( js_library( name = "style-code", - srcs = ["scripts/style-code.js"], + srcs = ["scripts/style-code.mjs"], visibility = ["//visibility:public"], deps = [ ":node_modules/ejs", diff --git a/MODULE.bazel b/MODULE.bazel index 97704d2e93b..caaef400729 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,23 +1,30 @@ module(name = "maplibre") 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 = "platforms", version = "0.0.10") 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_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( name = "npm", data = ["package.json"], pnpm_lock = "//:pnpm-lock.yaml", - update_pnpm_lock = True, + verify_node_modules_ignored = "//:.bazelignore", ) use_repo(npm, "npm") 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") provisioning_profile_repository = use_extension("@build_bazel_rules_apple//apple:apple.bzl", "provisioning_profile_repository_extension") diff --git a/package-lock.json b/package-lock.json index bea291bd5c8..dc0be11f074 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,9 +20,11 @@ "@mapbox/mvt-fixtures": "3.10.0", "@octokit/plugin-retry": "^7.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", - "csscolorparser": "~1.0.3", + "csscolorparser": "^1.0.3", "d3-queue": "3.0.7", "diff": "5.2.0", "ejs": "^3.1.10", @@ -2074,13 +2076,28 @@ "node": ">=14.0.0" } }, + "node_modules/@types/argparse": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-2.0.16.tgz", + "integrity": "sha512-aMqBra2JlqpFeCWOinCtpRpiCkPIXH8hahW2+FkGzvWjfE5sAqtOcrjN5DRcMnTQqFDe6gb1CVYuGnBH0lhXwA==", + "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.12.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", - "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", + "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": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.2" } }, "node_modules/@xmldom/xmldom": { @@ -2617,7 +2634,8 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/d3-queue": { "version": "3.0.7", @@ -5622,10 +5640,11 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" }, "node_modules/universal-user-agent": { "version": "7.0.2", diff --git a/package.json b/package.json index bfe97e40b78..69378454ca3 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,11 @@ "@mapbox/mvt-fixtures": "3.10.0", "@octokit/plugin-retry": "^7.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", - "csscolorparser": "~1.0.3", + "csscolorparser": "^1.0.3", "d3-queue": "3.0.7", "diff": "5.2.0", "ejs": "^3.1.10", @@ -42,5 +44,6 @@ "typescript": "^5.4.5", "xcode": "^3.0.1" }, - "scripts": {} + "scripts": {}, + "type": "module" } diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js deleted file mode 100755 index 1101bbc56be..00000000000 --- a/platform/android/scripts/generate-style-code.js +++ /dev/null @@ -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}) -); diff --git a/platform/darwin/BUILD.bazel b/platform/darwin/BUILD.bazel index 2ac4c6c3cfa..b40e9af341b 100644 --- a/platform/darwin/BUILD.bazel +++ b/platform/darwin/BUILD.bazel @@ -334,7 +334,7 @@ js_binary( "//:style-code", "//:style-spec", ], - entry_point = "scripts/generate-style-code.js", + entry_point = "scripts/generate-style-code.mjs", ) js_library( diff --git a/platform/darwin/scripts/generate-style-code.js b/platform/darwin/scripts/generate-style-code.mjs similarity index 97% rename from platform/darwin/scripts/generate-style-code.js rename to platform/darwin/scripts/generate-style-code.mjs index 6aa34d62954..1b79fa1b96c 100644 --- a/platform/darwin/scripts/generate-style-code.js +++ b/platform/darwin/scripts/generate-style-code.mjs @@ -1,14 +1,27 @@ -#!/usr/bin/env node -'use strict'; - -const { ArgumentParser } = require("argparse"); -const fs = require('fs'); -const path = require('path'); -const _ = require('lodash'); -const colorParser = require('csscolorparser'); -const assert = require('assert'); - -require('../../../scripts/style-code'); +import { ArgumentParser } from "argparse"; +import path from "node:path"; +import _ from "lodash"; +import colorParser from "csscolorparser"; +import assert from "assert"; + +import { readAndCompile, writeIfModified, camelize, unhyphenate } from "../../../scripts/style-code.mjs"; + +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 const args = (() => { @@ -22,11 +35,10 @@ const args = (() => { return parser.parse_args(); })(); -const cocoaConventions = require('./style-spec-cocoa-conventions-v8.json'); const prefix = 'MLN'; 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 { constructor(val) { @@ -787,7 +799,7 @@ const lightProperties = Object.keys(spec['light']).reduce((memo, name) => { const lightDoc = spec['light-cocoa-doc']; 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 layerH = readAndCompile('platform/darwin/src/MLNStyleLayer.h.ejs', root); diff --git a/platform/ios/scripts/style-spec.js b/platform/ios/scripts/style-spec.js deleted file mode 100644 index cca70ce6ead..00000000000 --- a/platform/ios/scripts/style-spec.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../..//scripts/style-spec-reference/v8'); diff --git a/platform/scripts/check-public-symbols.js b/platform/scripts/check-public-symbols.js index ac48729a013..57b6542975a 100644 --- a/platform/scripts/check-public-symbols.js +++ b/platform/scripts/check-public-symbols.js @@ -1,11 +1,7 @@ -#!/usr/bin/env node - -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const execFileSync = require('child_process').execFileSync; -const _ = require('lodash'); +import fs from "node:fs"; +import path from "node:path"; +import { execFileSync } from "node:child_process"; +import _ from "lodash"; const keyword = /\bMLN_EXPORT\b/; diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.mjs similarity index 81% rename from scripts/generate-style-code.js rename to scripts/generate-style-code.mjs index a1c431448d6..7471f5bff0b 100755 --- a/scripts/generate-style-code.js +++ b/scripts/generate-style-code.mjs @@ -1,13 +1,9 @@ -#!/usr/bin/env node -'use strict'; +import { ArgumentParser } from "argparse"; +import * as path from "node:path"; +import spec from "./style-spec.mjs"; +import colorParser from "csscolorparser"; -const { ArgumentParser } = require("argparse"); -const path = require('path'); -const fs = require('fs'); -const spec = require('./style-spec'); -const colorParser = require('csscolorparser'); - -require('./style-code'); +import { camelize, camelizeWithLeadingLowercase, readAndCompile, writeIfModified } from "./style-code.mjs"; // Parse command line const args = (() => { @@ -21,22 +17,33 @@ const args = (() => { return parser.parse_args(); })(); -function parseCSSColor(str) { +function parseCSSColor(/** @type {string} **/ str) { const color = colorParser.parseCSSColor(str); + if (!color) throw new Error("Could not parse CSS color"); return [ 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; }; -global.isOverridable = function (property) { +/** + * @param {any} property + */ +function isOverridable(property) { return ['text-color'].includes(property.name); }; -global.expressionType = function (property) { +/** + * @param {any} property + * @returns {string} + */ +function expressionType(property) { switch (property.type) { case 'boolean': 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)) { return 'TranslateAnchorType'; } @@ -102,7 +114,12 @@ global.evaluatedType = function (property) { } }; +/** + * @param {any} property + * @param {any} type + */ function attributeUniformType(property, type) { + /** @type {Record} **/ const attributeNameExceptions = { 'text-opacity': ['opacity'], 'icon-opacity': ['opacity'], @@ -120,6 +137,7 @@ function attributeUniformType(property, type) { 'fill-pattern': ['pattern_to', 'pattern_from'], 'fill-extrusion-pattern': ['pattern_to', 'pattern_from'] } + /** @type {string[]} **/ const names = attributeNameExceptions[property.name] || [ property.name.replace(type + '-', '').replace(/-/g, '_') ]; @@ -128,7 +146,10 @@ function attributeUniformType(property, type) { }).join(', '); } -global.layoutPropertyType = function (property) { +/** + * @param {any} property + */ +function layoutPropertyType(property) { switch (property['property-type']) { case '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']) { case 'data-driven': 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']) { case 'color-ramp': return `ColorRampPropertyValue`; @@ -162,6 +192,11 @@ global.propertyValueType = function (property) { } }; +/** + * @param {any} property + * @param {number} num + * @returns {string} + */ function formatNumber(property, num = 0) { if (evaluatedType(property) === "float") { const str = num.toString(); @@ -170,7 +205,10 @@ function formatNumber(property, num = 0) { return num.toString(); } -global.defaultValue = function (property) { +/** + * @param {any} property + */ +function defaultValue(property) { // https://github.com/mapbox/mapbox-gl-native/issues/5258 if (property.name === 'line-round-limit') { return 1; @@ -210,7 +248,7 @@ global.defaultValue = function (property) { return `{ ${color} }`; } 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) { return `{{${defaults.join(', ')}}}`; } else { @@ -222,7 +260,7 @@ global.defaultValue = function (property) { }; 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 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 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') { spec[`layout_${type}`][name].name = 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. 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; memo.push(spec[`paint_${type}`][name]); 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('-', '_'); writeIfModified(`src/mbgl/style/layers/${layerFileName}_layer_properties.hpp`, propertiesHpp(layer), outLocation); @@ -289,7 +350,8 @@ for (const layer of layers) { } // 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]; property.name = name; property['light-property'] = true; diff --git a/scripts/global.d.ts b/scripts/global.d.ts new file mode 100644 index 00000000000..8a3f5b10210 --- /dev/null +++ b/scripts/global.d.ts @@ -0,0 +1 @@ +declare module "csscolorparser" \ No newline at end of file diff --git a/scripts/jsconfig.json b/scripts/jsconfig.json index 451b267ff5e..7d51e37c635 100644 --- a/scripts/jsconfig.json +++ b/scripts/jsconfig.json @@ -3,9 +3,16 @@ "strict": true, "allowJs": true, "checkJs": true, - "module": "NodeNext" + "module": "NodeNext", + "skipLibCheck": true, + "resolveJsonModule": true, + "maxNodeModuleJsDepth": 0 }, "include": [ - "**/*.mjs" + "./**/*.mjs", + "**/*.d.ts" + ], + "exclude": [ + "node_modules" ] } \ No newline at end of file diff --git a/scripts/style-code.js b/scripts/style-code.mjs similarity index 62% rename from scripts/style-code.js rename to scripts/style-code.mjs index c3c92dc0df6..354dae88ec8 100644 --- a/scripts/style-code.js +++ b/scripts/style-code.mjs @@ -1,42 +1,63 @@ -// Global functions // +// EJS Helpers -const ejs = require('ejs'); -const fs = require('fs'); -const path = require('path'); +import ejs from "ejs"; +import fs from "node:fs"; -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 : ""; }; -global.camelize = function (str) { +/** + * @param {string} str + * @returns {string} + */ +export function camelize(str) { return str.replace(/(?:^|-)(.)/g, function (_, x) { return x.toUpperCase(); }); }; -global.camelizeWithLeadingLowercase = function (str) { +/** + * @param {string} str + * @returns {string} + */ +export function camelizeWithLeadingLowercase(str) { return str.replace(/-(.)/g, function (_, x) { return x.toUpperCase(); }); }; -global.snakeCaseUpper = function snakeCaseUpper(str) { +export function snakeCaseUpper(/** @type {string} **/ str) { return str.replace(/-/g, "_").toUpperCase(); }; -global.unhyphenate = function (str) { +export function unhyphenate (/** @type {string} **/ str) { return str.replace(/-/g, " "); }; // 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() { 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}`); 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) { filename = path.resolve(path.join(output, filename)); } @@ -61,7 +82,13 @@ global.writeIfModified = function(filename, newContent, output) { 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) { filename = path.resolve(path.join(root, filename)); } diff --git a/scripts/style-spec-reference/latest.js b/scripts/style-spec-reference/latest.mjs similarity index 100% rename from scripts/style-spec-reference/latest.js rename to scripts/style-spec-reference/latest.mjs diff --git a/scripts/style-spec.js b/scripts/style-spec.mjs similarity index 89% rename from scripts/style-spec.js rename to scripts/style-spec.mjs index 48c8b144b19..2d497938a44 100644 --- a/scripts/style-spec.js +++ b/scripts/style-spec.mjs @@ -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"] = {}; -referenceSpec["layout_location-indicator"] = { +/** @type {any} */ +let modifiedReferenceSpec = referenceSpec; + +modifiedReferenceSpec.layer.type.values["location-indicator"] = {}; +modifiedReferenceSpec["layout_location-indicator"] = { "top-image": { "type": "resolvedImage", "property-type": "data-constant", @@ -37,7 +40,7 @@ referenceSpec["layout_location-indicator"] = { } }; -referenceSpec["paint_location-indicator"] = { +modifiedReferenceSpec["paint_location-indicator"] = { "perspective-compensation": { "type": "number", "default": "0.85", @@ -65,7 +68,6 @@ referenceSpec["paint_location-indicator"] = { }, "bearing": { "type": "number", - "default": "0", "default": 0, "period": 360, "units": "degrees", @@ -176,21 +178,7 @@ referenceSpec["paint_location-indicator"] = { }, "transition": true, "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; \ No newline at end of file diff --git a/shaders/generate_shader_code.js b/shaders/generate_shader_code.mjs similarity index 98% rename from shaders/generate_shader_code.js rename to shaders/generate_shader_code.mjs index ba15163a6b0..5bfb41beca4 100644 --- a/shaders/generate_shader_code.js +++ b/shaders/generate_shader_code.mjs @@ -1,12 +1,8 @@ -#!/usr/bin/env node -'use strict'; - console.log("Generating shaders..."); -const { ArgumentParser } = require("argparse"); -const path = require("node:path"); -const fs = require("node:fs") -const os = require("node:os"); +import { ArgumentParser } from "argparse"; +import path from "node:path"; +import fs from "node:fs"; const generatedHeader = `// Generated code, do not modify this file!`; @@ -220,7 +216,7 @@ const args = (() => { // Generate shader source headers -const root = path.dirname(__dirname); +const root = path.dirname(import.meta.dirname); const outLocation = args.out ? args.out : root; const shaderRoot = path.join(root, "shaders"); const outputRoot = path.join(outLocation, "include/mbgl/shaders"); diff --git a/test/BUILD.bazel b/test/BUILD.bazel index 2848fce98cf..94d2a64ea0d 100644 --- a/test/BUILD.bazel +++ b/test/BUILD.bazel @@ -87,4 +87,4 @@ cc_test( "tests", "//platform/linux:impl", ], -) +) \ No newline at end of file diff --git a/test/storage/server.js b/test/storage/server.js index cfacf7ce99a..a7de0944d4a 100755 --- a/test/storage/server.js +++ b/test/storage/server.js @@ -1,6 +1,7 @@ -#!/usr/bin/env node -/* jshint node: true */ -'use strict'; +import express from "express"; +import path from "node:path"; + +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. // 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('end', function() { process.exit(0); }); - -var fs = require('fs'); -var express = require('express'); var app = express(); // 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) { - 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.status(200).send(); // res.send('Request ' + req.params.style);