diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index ee4e4e4174..0000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,17 +0,0 @@ -module.exports = { - env: { - browser: true, - es2021: true, - node: true - }, - overrides: [ - ], - parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module' - }, - rules: { - eqeqeq: 'off', - quotes: ['error', 'single' , { 'allowTemplateLiterals': true }] - } -} \ No newline at end of file diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 51f2b0049d..5168b0bf49 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -13,10 +13,10 @@ name: "CodeQL" on: push: - branches: [ "main" ] + branches: [ "main", "major", "minor", "patch" ] pull_request: # The branches below must be a subset of the branches above - branches: [ "main" ] + branches: [ "main", "major", "minor", "patch" ] schedule: - cron: '40 1 * * 2' diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index b6d664e96a..2cf63fc48e 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -4,7 +4,7 @@ name: Deploy static content to Pages on: # Runs on pushes targeting the default branch push: - branches: ["main"] + branches: [ "main" ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml new file mode 100644 index 0000000000..273937cba4 --- /dev/null +++ b/.github/workflows/eslint.yml @@ -0,0 +1,24 @@ +name: ESLint + +on: + push: + branches: [ "main", "major", "minor", "patch" ] + pull_request: + branches: [ "main", "major", "minor", "patch" ] + +jobs: + eslint: + name: Run ESLint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm install + + - name: Run ESLint + run: npx eslint . --max-warnings=0 \ No newline at end of file diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 1b2e6c9994..d9701b774c 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -2,9 +2,9 @@ name: Codi unit tests on: push: - branches: [ main ] + branches: [ "main", "major", "minor", "patch" ] pull_request: - branches: [ main ] + branches: [ "main", "major", "minor", "patch" ] jobs: build: diff --git a/DEVELOPING.md b/DEVELOPING.md index 712065ab46..0fc0e5cf94 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -36,6 +36,23 @@ ESBuild must also be used to compile the CSS supporting the MAPP and MAPP.UI ele npx esbuild --bundle public/css/_ui.css --outfile=public/css/ui.css --loader:.svg=dataurl +## ESLint + +The codebase makes use of the [eslint](eslint.org) package to ensure that our code adhere to different rules and coding guidelines. +To run `eslint` you will need to have the development packages installed. You can ensure they are installed by running `npm install` in the root of the xyz directory. + +To run the lint you can execute `npx eslint .` in the root of the application. This will show any issues there are with the codebase. You can also add the flag `--fix` to the command to allow eslint to fix any issues it may find. + +eslint command + + npx esbuild . + +eslint command with fix + + npx esbuild . --fix + +There are other extensions you can use in your editor to get on the fly error highlighting where any rules are broken. Please look into what eslint supports in your environment. + ## version.js hash The mapp module object holds a hash of the latest release commit which can be generated by executing the version.js script in the root. diff --git a/README.md b/README.md index 8832e6bc1a..d31ee20fdc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -**v4.12.1α** +**v4.12.1** [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) ![Codi Unit Tests](https://github.com/GEOLYTIX/xyz/actions/workflows/unit_tests.yml/badge.svg) diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000000..d5cb190633 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,22 @@ +export default [ + { + ignores: ['public/js/lib/*', 'docs/**/*'], + }, + { + files: ['**/*.js', '**/*.mjs'], + rules: { + quotes: ['error', 'single', { 'allowTemplateLiterals': true }], + 'prefer-const': ['error', { + 'destructuring': 'any', + 'ignoreReadBeforeAssign': true + }], + 'max-depth': ['error', + { + 'max': 4 + } + ], + // 'complexity': ['error', { 'max': 15 }], + 'no-nested-ternary': 'error' + } + } +]; \ No newline at end of file diff --git a/lib/layer/decorate.mjs b/lib/layer/decorate.mjs index 0b87795e4b..0a3dad0e67 100644 --- a/lib/layer/decorate.mjs +++ b/lib/layer/decorate.mjs @@ -249,7 +249,7 @@ async function zoomToExtent(params) { // Zooms to a specific extent. // XMLHttpRequest to layer extent endpoint - let response = await mapp.utils.xhr(`${this.mapview.host}/api/query/layer_extent?` + + const response = await mapp.utils.xhr(`${this.mapview.host}/api/query/layer_extent?` + mapp.utils.paramString({ // build query string for the url locale: this.mapview.locale.key, layer: this.key, diff --git a/lib/layer/featureFields.mjs b/lib/layer/featureFields.mjs index 866af75856..abac8a6c14 100644 --- a/lib/layer/featureFields.mjs +++ b/lib/layer/featureFields.mjs @@ -84,9 +84,9 @@ The jenks distribution method requires the stats.jenks utility method to calcula @param {layer} layer A decorated mapp layer object. */ function jenks(layer) { - let theme = layer.style.theme; + const theme = layer.style.theme; - let n = Math.min(layer.featureFields[theme.field].values.length, theme.categories.length); + const n = Math.min(layer.featureFields[theme.field].values.length, theme.categories.length); // Parse array values as float. layer.featureFields[theme.field].values = layer.featureFields[theme.field].values.map(parseFloat); @@ -113,7 +113,7 @@ The count distribution method counts values in the `featureFields.values[]` arra @param {layer} layer A decorated mapp layer object. */ function count(layer) { - let theme = layer.style.theme; + const theme = layer.style.theme; layer.featureFields[theme.field].values.forEach(val => { diff --git a/lib/layer/featureStyle.mjs b/lib/layer/featureStyle.mjs index 4901fb5ed4..96ab042d2a 100644 --- a/lib/layer/featureStyle.mjs +++ b/lib/layer/featureStyle.mjs @@ -144,7 +144,7 @@ export default function featureStyle(layer) { if (!layer.style.cluster) return; - let clusterScale = parseFloat(layer.style.cluster.clusterScale) + const clusterScale = parseFloat(layer.style.cluster.clusterScale) // Spread cluster style into feature.style. feature.style = { diff --git a/lib/layer/themes/distributed.mjs b/lib/layer/themes/distributed.mjs index ffcf28376c..1c04b91893 100644 --- a/lib/layer/themes/distributed.mjs +++ b/lib/layer/themes/distributed.mjs @@ -25,7 +25,7 @@ export default function(theme, feature) { theme.index = 0 } - let field = theme.field || 'id' + const field = theme.field || 'id' // Get feature identifier for theme. const val = feature.properties[field] diff --git a/lib/layer/themes/graduated.mjs b/lib/layer/themes/graduated.mjs index 7c55464c8d..a87b555607 100644 --- a/lib/layer/themes/graduated.mjs +++ b/lib/layer/themes/graduated.mjs @@ -20,7 +20,7 @@ export default function (theme, feature) { // The graduated theme requires feature.properties. if (!feature.properties) return; - let catValue = Array.isArray(feature.properties.features) ? + const catValue = Array.isArray(feature.properties.features) ? // Reduce array of features to sum catValue feature.properties.features.reduce((total, F) => total + Number(F.getProperties()[theme.field]), 0) : @@ -35,9 +35,9 @@ export default function (theme, feature) { 'greater_than': val => cat => val >= cat.value } - let index = theme.categories.findIndex(graduated_breaks[theme.graduated_breaks](catValue)) + const index = theme.categories.findIndex(graduated_breaks[theme.graduated_breaks](catValue)) - let cat = theme.categories.at(index) + const cat = theme.categories.at(index) // Spread cat style to retain scale property feature.style = { diff --git a/lib/location/create.mjs b/lib/location/create.mjs index 2db40efdb0..4b5a682dd4 100644 --- a/lib/location/create.mjs +++ b/lib/location/create.mjs @@ -79,7 +79,7 @@ export default async function createLocation(feature, interaction, layer) { setTimeout(checkFeature, 1000); function checkFeature() { - let found = layer.features?.find(F => F.properties?.id === location.id); + const found = layer.features?.find(F => F.properties?.id === location.id); if (found) { layer.source.un('tileloadend', concatFeatures); } else { diff --git a/lib/mapp.mjs b/lib/mapp.mjs index ee87300e1b..c86c92ba8e 100644 --- a/lib/mapp.mjs +++ b/lib/mapp.mjs @@ -57,7 +57,7 @@ if (window.ol === undefined) { } else { - let olVersion = parseFloat(ol?.util.VERSION) + const olVersion = parseFloat(ol?.util.VERSION) console.log(`OpenLayers version ${olVersion}`) @@ -70,9 +70,9 @@ if (window.ol === undefined) { self.mapp = { ol: _ol, - version: '4.12.1α', + version: '4.12.1', - hash: 'dcf15d344c332b668212c7cee1b967d0bd973393', + hash: 'd18842e399cba41278b14e6658d2706a692f9b19', host: document.head?.dataset?.dir || '', diff --git a/lib/plugins/login.mjs b/lib/plugins/login.mjs index 46c242174d..71fad68643 100644 --- a/lib/plugins/login.mjs +++ b/lib/plugins/login.mjs @@ -25,6 +25,6 @@ export function login(plugin, mapview) { btnColumn.appendChild(mapp.utils.html.node` + href=${mapp.user ? '?logout=true' : '?login=true'}>
`); } \ No newline at end of file diff --git a/lib/ui/Gazetteer.mjs b/lib/ui/Gazetteer.mjs index 0821770836..0922a327ac 100644 --- a/lib/ui/Gazetteer.mjs +++ b/lib/ui/Gazetteer.mjs @@ -42,7 +42,7 @@ export default gazetteer => { if (!e.target.value.length) return; // Get possible coordinates from input. - let ll = e.target.value.split(',').map(parseFloat) + const ll = e.target.value.split(',').map(parseFloat) // Check whether coordinates are valid float values. if (ll.length === 2 && ll.every(n => typeof n === 'number' && !isNaN(n) && isFinite(n))) { diff --git a/lib/ui/elements/legendIcon.mjs b/lib/ui/elements/legendIcon.mjs index 95e9436691..d101be39ee 100644 --- a/lib/ui/elements/legendIcon.mjs +++ b/lib/ui/elements/legendIcon.mjs @@ -72,7 +72,7 @@ function createIconFromArray(style) { }); }; - let legendScale = style.icon[0].legendScale || 1; + const legendScale = style.icon[0].legendScale || 1; style.icon.forEach((icon) => { diff --git a/lib/ui/layers/filters.mjs b/lib/ui/layers/filters.mjs index 62942be85f..853e48ccc4 100644 --- a/lib/ui/layers/filters.mjs +++ b/lib/ui/layers/filters.mjs @@ -522,7 +522,7 @@ async function filter_in(layer, filter) { const pattern = e.target.value; - let filtered = filter[filter.type].filter(val => + const filtered = filter[filter.type].filter(val => // val may not be string. val.toString().toLowerCase().startsWith(pattern.toLowerCase())) diff --git a/lib/ui/layers/legends/categorized.mjs b/lib/ui/layers/legends/categorized.mjs index 743ebeb285..e01ba357c6 100644 --- a/lib/ui/layers/legends/categorized.mjs +++ b/lib/ui/layers/legends/categorized.mjs @@ -85,7 +85,7 @@ export default function categorizedTheme(layer) { const cat_label = cat.label + (cat.count? ` [${cat.count}]`:'') // Cat label with filter function. - let label = mapp.utils.html`
catToggle(e, layer, cat)}>${cat_label}` diff --git a/lib/ui/locations/entries/cloudinary.mjs b/lib/ui/locations/entries/cloudinary.mjs index 4283b9adc7..578f869802 100644 --- a/lib/ui/locations/entries/cloudinary.mjs +++ b/lib/ui/locations/entries/cloudinary.mjs @@ -186,10 +186,10 @@ function imageLoad(e, entry) { img.onload = async () => { - let - canvas = mapp.utils.html.node``, - max_size = 1024, - width = img.width, + const canvas = mapp.utils.html.node`` + const max_size = 1024 + + let width = img.width, height = img.height // resize @@ -276,7 +276,7 @@ async function docLoad(e, entry) { async function trash(e, entry) { - const confirm = await mapp.ui.elements.confirm({text: mapp.dictionary.remove_item_confirm}); + const confirm = await mapp.ui.elements.confirm({ text: mapp.dictionary.remove_item_confirm }); if (!confirm) return; diff --git a/lib/utils/copyToClipboard.mjs b/lib/utils/copyToClipboard.mjs index 9bac15f02e..cfd1a17fb0 100644 --- a/lib/utils/copyToClipboard.mjs +++ b/lib/utils/copyToClipboard.mjs @@ -7,7 +7,7 @@ // Create temporary textarea to copy string to clipboard. export function copyToClipboard(str) { - let textArea = document.body.appendChild(mapp.utils.html.node` + const textArea = document.body.appendChild(mapp.utils.html.node`