diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..7cb66ce --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,35 @@ +version: 2 + +updates: + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 5 + groups: + dev-dependencies: + dependency-type: development + update-types: + - minor + - patch + production-dependencies: + dependency-type: production + update-types: + - minor + - patch + commit-message: + prefix: "deps" + include: scope + + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + day: monday + groups: + actions: + patterns: + - "*" + commit-message: + prefix: "ci" diff --git a/.github/workflows/build-sigplot.yml b/.github/workflows/build-sigplot.yml index 371bc6e..d3b98cb 100644 --- a/.github/workflows/build-sigplot.yml +++ b/.github/workflows/build-sigplot.yml @@ -1,53 +1,66 @@ -name: Build +name: CI on: push: - branches: [ master, develop ] + branches: [main, master, develop] tags: - - '*' + - "*" pull_request: - branches: [ master, develop ] + branches: [main, master, develop] jobs: - build: + lint: runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [12.x, 14.x, 16.x] - steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run lint -- --max-warnings=999 + - run: npm run format:check || echo "::warning::Formatting issues found. Run 'npm run format' to fix." - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 - with: - node-version: ${{ matrix.node-version }} - cache: 'npm' + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run build + - uses: actions/upload-artifact@v4 + with: + name: dist + path: dist/ - - name: Install dependencies - run: npm install -q + test: + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - run: npm run test - - name: Build SigPlot - run: npx grunt dist - publish: - needs: build + needs: [lint, build, test] if: startsWith(github.ref, 'refs/tags') runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - uses: actions/setup-node@v2 - with: - node-version: 12 - cache: 'npm' - - - run: npm install - - run: npm run build - - uses: JS-DevTools/npm-publish@v1 - with: - token: ${{ secrets.NPM_TOKEN }} - - if: steps.publish.outputs.type != 'none' - run: | - echo "Version changed: ${{ steps.publish.outputs.old-version }} => ${{ steps.publish.outputs.version }}" + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + registry-url: https://registry.npmjs.org + - run: npm ci + - run: npm run build + - run: npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index 8c339c4..5c7e4f8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,120 +1,34 @@ -*.o -*.so -*.exe +# Dependencies +node_modules/ + +# Build output +dist/ +dist/types/ + +# Generated docs +doc/ + +# OS files .DS_Store *~ -*.gitignore -Git_History -node_modules -/C:\nppdf32Log\debuglog.txt -.DS_Store -doc/ + +# Editor files .idea/ +*.swp +.vscode/ # Logs -logs *.log npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov +# Coverage +coverage/ +.nyc_output/ -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file +# Environment .env .env.test -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Vim Swap Files -*.swp +# IDE caches +.eslintcache +*.tsbuildinfo diff --git a/.jsbeautifyrc b/.jsbeautifyrc deleted file mode 100644 index 0809af6..0000000 --- a/.jsbeautifyrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "indentSize": 4, - "indentWithTabs": false, - "wrapLineLength": 0, - "eol": "\n" -} diff --git a/.jshintignore b/.jshintignore deleted file mode 100644 index ffc6211..0000000 --- a/.jshintignore +++ /dev/null @@ -1,6 +0,0 @@ -js/typedarray.js -js/CanvasInput.js -js/spin.js -js/tinycolor.js -js/loglevel.js -js/slider.js diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 8c86fc7..0000000 --- a/.jshintrc +++ /dev/null @@ -1,14 +0,0 @@ -{ - "curly": true, - "eqeqeq": true, - "immed": true, - "latedef": true, - "newcap": true, - "noarg": true, - "sub": true, - "undef": true, - "unused": true, - "boss": true, - "eqnull": true, - "node": true -} diff --git a/.npmignore b/.npmignore deleted file mode 100644 index e69de29..0000000 diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index 2bf5ad0..0000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -stable diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..cbf5749 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,6 @@ +node_modules/ +dist/ +doc/ +benchmark/ +support/ +*.min.js diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0e9400f --- /dev/null +++ b/.prettierrc @@ -0,0 +1,11 @@ +{ + "tabWidth": 4, + "useTabs": false, + "semi": true, + "singleQuote": false, + "trailingComma": "none", + "printWidth": 120, + "endOfLine": "lf", + "bracketSpacing": true, + "arrowParens": "always" +} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6d82eb4..0000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: node_js -node_js: - - "10.15.1" - -services: - - docker - -before_install: - - rm -rf dist/* - - npm install -g grunt - - npm install -q -script: - - grunt dist - -notifications: - email: - on_success: never - diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index 69d5292..0000000 --- a/Gruntfile.js +++ /dev/null @@ -1,276 +0,0 @@ -'use strict'; - -module.exports = function (grunt) { - - // Project configuration. - grunt.initConfig({ - // Metadata. - pkg: grunt.file.readJSON('package.json'), - jshint: { - options: { - jshintrc: '.jshintrc' - }, - gruntfile: { - src: 'Gruntfile.js' - }, - js: { - options: { - jshintrc: 'js/.jshintrc' - }, - src: ['js/**/*.js', 'test/tests*.js'] - }, - }, - qunit: { - options: { '--web-security': 'no', '--local-to-remote-url-access': 'yes' }, - all: ['test/test_headless.html'] - }, - 'closure-compiler': { - sigplot_debug: { - closurePath: 'support/google-closure-compiler', - js: 'dist/sigplot.js', - maxBuffer: 500, - jsOutputFile: 'dist/sigplot-debug.js', - options: { - formatting: 'PRETTY_PRINT', - compilation_level: 'WHITESPACE_ONLY', - } - }, - sigplot_plugins_debug: { - closurePath: 'support/google-closure-compiler', - js: 'dist/sigplot.plugins.js', - maxBuffer: 500, - jsOutputFile: 'dist/sigplot.plugins-debug.js', - options: { - formatting: 'PRETTY_PRINT', - compilation_level: 'WHITESPACE_ONLY' - } - }, - sigplot_minimized: { - closurePath: 'support/google-closure-compiler', - js: 'dist/sigplot.js', - maxBuffer: 500, - jsOutputFile: 'dist/sigplot-minimized.js', - options: { - compilation_level: 'SIMPLE_OPTIMIZATIONS' - } - }, - sigplot_plugins_minimized: { - closurePath: 'support/google-closure-compiler', - js: 'dist/sigplot.plugins.js', - maxBuffer: 500, - jsOutputFile: 'dist/sigplot.plugins-minimized.js', - options: { - compilation_level: 'SIMPLE_OPTIMIZATIONS' - } - } - }, - jsdoc: { - sigplot: { - src: ['js/*.js'], - options: { - destination: 'doc', - template: './node_modules/minami/', - configure: '.jsdoc.json' - } - } - }, - clean: { - build: ["dist/**/*", "!dist/*.zip"], - doc: ["doc/**/*"] - }, - compress: { - main: { - options: { - archive: "dist/sigplot-<%= pkg.version %>-<%= grunt.template.today('yyyy-mm-dd') %>.zip", - }, - files: [ - {expand: true, cwd: 'dist/', src: ['*-debug.js'], dest: 'sigplot-<%= pkg.version %>'}, - {expand: true, cwd: 'dist/', src: ['*-minimized.js'], dest: 'sigplot-<%= pkg.version %>'}, - {src: ['doc/**/*'], dest: 'sigplot-<%= pkg.version %>'} - ] - } - }, - githash: { - main: { - options: {} - } - }, - replace: { - version: { - src: ["dist/*.js"], - overwrite: true, - replacements: [{ - from: /version-PLACEHOLDER/g, - to: "<%= pkg.version %>-<%= githash.main.short %>", - }], - } - }, - 'http-server': { - 'test': { - cache: 0, - port: 1337 - }, - }, - jsbeautifier: { - check: { - // Only check a subset of the files - src: [ - 'js/m.js', - 'js/mx.js', - 'js/sigplot.layer1d.js', - 'js/sigplot.layer2d.js', - 'js/sigplot.js', - 'js/sigplot.annotations.js', - 'js/sigplot.slider.js', - 'js/sigplot.accordion.js', - 'js/sigplot.boxes.js', - 'js/sigplot.playback.js', - 'js/sigplot.plugin.js', - 'test/tests.js', - 'test/tests.colormap.js', - 'test/tests.interactive-accordion.js', - 'test/tests.interactive-appearance.js', - 'test/tests.interactive-boxes.js', - 'test/tests.interactive-core.js', - 'test/tests.interactive-cuts.js', - 'test/tests.interactive-layer1d.js', - 'test/tests.interactive-layer2d.js', - 'test/tests.interactive-menu.js', - 'test/tests.interactive-mouse.js', - 'test/tests.interactive-slider.js', - 'test/tests.interactive-symbols.js', - 'test/tests.m.js', - 'test/tests.mx.js', - 'test/tests.sigplot.js' - ], - options: { - mode: "VERIFY_ONLY", - config: ".jsbeautifyrc" - } - }, - cleanup: { - // Only cleanup a subset of the files - src: [ - 'js/m.js', - 'js/mx.js', - 'js/sigplot.layer1d.js', - 'js/sigplot.layer2d.js', - 'js/sigplot.js', - 'js/sigplot.annotations.js', - 'js/sigplot.slider.js', - 'js/sigplot.accordion.js', - 'js/sigplot.boxes.js', - 'js/sigplot.playback.js', - 'js/sigplot.plugin.js', - 'test/tests.js', - 'test/tests.colormap.js', - 'test/tests.interactive-accordion.js', - 'test/tests.interactive-appearance.js', - 'test/tests.interactive-boxes.js', - 'test/tests.interactive-core.js', - 'test/tests.interactive-cuts.js', - 'test/tests.interactive-layer1d.js', - 'test/tests.interactive-layer2d.js', - 'test/tests.interactive-menu.js', - 'test/tests.interactive-mouse.js', - 'test/tests.interactive-slider.js', - 'test/tests.interactive-symbols.js', - 'test/tests.m.js', - 'test/tests.mx.js', - 'test/tests.sigplot.js' - ], - options: { - config: ".jsbeautifyrc" - } - } - }, - express: { - test: { - options: { - script: 'benchmark/express.js' - } - } - }, - karma: { - bench: { - configFile: 'karma.conf.js' - } - }, - browserify: { - sigplot: { - src: 'js/sigplot.js', - dest: 'dist/sigplot.js', - options: { - browserifyOptions: { - standalone: 'sigplot', - debug: true - }, - transform: [ - [ - 'babelify', { - "presets": ["@babel/preset-env"] - } - ] - ] - } - }, - plugins: { - src: [ 'js/plugins.js' ], - dest: 'dist/sigplot.plugins.js', - options: { - browserifyOptions: { - standalone: 'sigplot_plugins', - debug: true - }, - transform: [ - [ - 'babelify', { - "presets": ["@babel/preset-env"] - } - ] - ] - } - } - } - }); - - // These plugins provide necessary tasks. - grunt.loadNpmTasks('grunt-closure-compiler'); - grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.loadNpmTasks('grunt-contrib-qunit'); - grunt.loadNpmTasks('grunt-jsdoc'); - grunt.loadNpmTasks('grunt-contrib-clean'); - grunt.loadNpmTasks('grunt-contrib-compress'); - grunt.loadNpmTasks('grunt-http-server'); - grunt.loadNpmTasks('grunt-jsbeautifier'); - grunt.loadNpmTasks('grunt-karma'); - grunt.loadNpmTasks('grunt-express-server'); - grunt.loadNpmTasks('grunt-browserify'); - grunt.loadNpmTasks('grunt-text-replace'); - grunt.loadNpmTasks('grunt-githash'); - - grunt.registerTask('build', ['jsbeautifier:check', 'jshint', 'browserify', 'githash', 'replace']); - - // Check everything is good - grunt.registerTask('test', ['build', 'qunit']); - - // Beautify the code - grunt.registerTask('prep', ['jsbeautifier:cleanup']); - - // Generate documentation - grunt.registerTask('generate-docs', ['jsdoc']); - - // Build a distributable release - grunt.registerTask('dist', ['clean', 'test', 'closure-compiler', 'jsdoc', 'compress']); - - // Default task. - grunt.registerTask('default', 'test'); - - // Benchmark in browsers. - grunt.registerTask('benchtest', ['express:test', 'karma:bench']); - grunt.registerTask('build_and_test', ['build', 'benchtest']); - - // for compatibility with the old grunt commands - grunt.registerTask('web_server', 'http-server'); - -}; diff --git a/dist/.gitignore b/dist/.gitignore deleted file mode 100644 index f1a9653..0000000 --- a/dist/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.js.report.txt -*.js diff --git a/dist/.npmignore b/dist/.npmignore deleted file mode 100644 index 50543e5..0000000 --- a/dist/.npmignore +++ /dev/null @@ -1,2 +0,0 @@ -*.js.report.txt -*.zip diff --git a/esbuild.config.mjs b/esbuild.config.mjs new file mode 100644 index 0000000..5904ed3 --- /dev/null +++ b/esbuild.config.mjs @@ -0,0 +1,57 @@ +import * as esbuild from "esbuild"; + +const common = { + bundle: true, + target: "es2018", + sourcemap: true, + logLevel: "info", +}; + +// UMD-style bundle (IIFE with global name) — replaces browserify --standalone +await esbuild.build({ + ...common, + entryPoints: ["js/sigplot.ts"], + format: "iife", + globalName: "sigplot", + outfile: "dist/sigplot.js", + footer: { js: "sigplot = sigplot.default || sigplot;" }, +}); + +// Minified UMD +await esbuild.build({ + ...common, + entryPoints: ["js/sigplot.ts"], + format: "iife", + globalName: "sigplot", + outfile: "dist/sigplot.min.js", + minify: true, + footer: { js: "sigplot = sigplot.default || sigplot;" }, +}); + +// ESM bundle for modern consumers +await esbuild.build({ + ...common, + entryPoints: ["js/sigplot.ts"], + format: "esm", + outfile: "dist/sigplot.esm.js", + target: "es2020", +}); + +// Plugins bundle +await esbuild.build({ + ...common, + entryPoints: ["js/plugins.ts"], + format: "iife", + globalName: "sigplot_plugins", + outfile: "dist/sigplot.plugins.js", +}); + +// Plugins minified +await esbuild.build({ + ...common, + entryPoints: ["js/plugins.ts"], + format: "iife", + globalName: "sigplot_plugins", + outfile: "dist/sigplot.plugins.min.js", + minify: true, +}); diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..6212525 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,157 @@ +import js from "@eslint/js"; +import prettier from "eslint-config-prettier"; +import tsParser from "@typescript-eslint/parser"; +import tsPlugin from "@typescript-eslint/eslint-plugin"; + +export default [ + js.configs.recommended, + + // Source files + { + files: ["js/**/*.js", "js/**/*.ts"], + languageOptions: { + ecmaVersion: 2022, + sourceType: "module", + globals: { + // Browser globals + window: "readonly", + document: "readonly", + navigator: "readonly", + console: "readonly", + setTimeout: "readonly", + clearTimeout: "readonly", + setInterval: "readonly", + clearInterval: "readonly", + requestAnimationFrame: "readonly", + cancelAnimationFrame: "readonly", + CustomEvent: "readonly", + Event: "readonly", + ImageData: "readonly", + HTMLCanvasElement: "readonly", + HTMLElement: "readonly", + XMLHttpRequest: "readonly", + Worker: "readonly", + WebSocket: "readonly", + URL: "readonly", + Image: "readonly", + Blob: "readonly", + FileReader: "readonly", + prompt: "readonly", + alert: "readonly", + performance: "readonly", + fetch: "readonly", + Headers: "readonly", + Response: "readonly", + HTMLVideoElement: "readonly", + HTMLImageElement: "readonly", + self: "readonly", + event: "readonly", + + // Typed arrays + ArrayBuffer: "readonly", + SharedArrayBuffer: "readonly", + Float32Array: "readonly", + Float64Array: "readonly", + Int8Array: "readonly", + Int16Array: "readonly", + Int32Array: "readonly", + Uint8Array: "readonly", + Uint8ClampedArray: "readonly", + Uint16Array: "readonly", + Uint32Array: "readonly", + DataView: "readonly", + WeakMap: "readonly", + ResizeObserver: "readonly", + process: "readonly", + global: "readonly", + }, + }, + rules: { + // Port from JSHint — warn for now, upgrade to error after code cleanup + eqeqeq: "warn", + curly: "error", + "no-caller": "error", + "new-cap": "warn", + "no-undef": "error", + "no-fallthrough": "warn", + + // Relaxed for existing code — tighten later + "no-unused-vars": ["warn", { args: "none", varsIgnorePattern: "^_" }], + "no-redeclare": "warn", + "no-empty": "warn", + "no-prototype-builtins": "off", + "no-cond-assign": "off", // JSHint "boss" mode + "no-useless-assignment": "warn", + "no-func-assign": "warn", + "no-self-assign": "warn", + "no-useless-escape": "warn", + "no-unassigned-vars": "warn", + + // Don't enforce yet — enable during ES module migration + "no-var": "off", + "prefer-const": "off", + }, + }, + + // Test files + { + files: ["test/**/*.js"], + languageOptions: { + ecmaVersion: 2022, + sourceType: "script", + globals: { + window: "readonly", + document: "readonly", + console: "readonly", + setTimeout: "readonly", + QUnit: "readonly", + sigplot: "readonly", + Float32Array: "readonly", + Float64Array: "readonly", + Int32Array: "readonly", + ArrayBuffer: "readonly", + DataView: "readonly", + Uint8Array: "readonly", + Blob: "readonly", + URL: "readonly", + XMLHttpRequest: "readonly", + Image: "readonly", + HTMLCanvasElement: "readonly", + }, + }, + rules: { + "no-undef": "error", + "no-unused-vars": "off", + }, + }, + + // TypeScript source files + { + files: ["js/**/*.ts"], + languageOptions: { + parser: tsParser, + parserOptions: { + ecmaVersion: 2022, + sourceType: "module", + }, + }, + plugins: { + "@typescript-eslint": tsPlugin, + }, + rules: { + // Use TypeScript-aware no-unused-vars instead of base rule + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": [ + "warn", + { args: "none", varsIgnorePattern: "^_" }, + ], + // TypeScript handles undefined variable checking — disable ESLint's + "no-undef": "off", + // TypeScript allows re-declarations in certain patterns (overloads, etc.) + "no-redeclare": "off", + }, + }, + + // Disable rules that conflict with Prettier + prettier, +]; diff --git a/examples/index.html b/examples/index.html index 490f218..4d06b9f 100644 --- a/examples/index.html +++ b/examples/index.html @@ -4,10 +4,10 @@