From c71df0f6b70c9e03eb8c41b1ec26c1eb9ad375e5 Mon Sep 17 00:00:00 2001 From: Patrick Organ Date: Sun, 10 Nov 2024 15:11:21 -0500 Subject: [PATCH 01/20] upgrade eslint and config to v9 --- .eslintrc.js | 25 ------------------------- eslint.config.mjs | 41 +++++++++++++++++++++++++++++++++++++++++ package.json | 9 +++++---- 3 files changed, 46 insertions(+), 29 deletions(-) delete mode 100644 .eslintrc.js create mode 100644 eslint.config.mjs diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index f6e5abe..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,25 +0,0 @@ -module.exports = { - parser: '@typescript-eslint/parser', - parserOptions: { - ecmaVersion: 2020, - sourceType: 'module', - }, - env: { - node: true, - browser: true, - commonjs: true, - }, - settings: {}, - extends: ['plugin:@typescript-eslint/recommended', 'eslint:recommended'], - rules: { - '@typescript-eslint/ban-ts-comment': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/no-empty-function': 'off', - '@typescript-eslint/no-explicit-any': 'off', - '@typescript-eslint/no-var-requires': 'off', - indent: ['error', 4, { SwitchCase: 1 }], - 'newline-per-chained-call': ['error', { ignoreChainWithDepth: 2 }], - 'no-useless-escape': 'off', - }, -}; diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..2dae642 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,41 @@ +import tsParser from '@typescript-eslint/parser'; +import tsPlugin from '@typescript-eslint/eslint-plugin'; +import globals from "globals"; +import { Linter } from 'eslint'; + +/** @type {Linter.Config} */ +const config = [ + { + files: ["src/**/*.js", "src/**/*.ts"], + languageOptions: { + ecmaVersion: 2022, + sourceType: "script", + parser: tsParser, + parserOptions: { + ecmaVersion: 2020, + sourceType: 'module', + }, + globals: { + ...globals.browser, + ...globals.node, + }, + }, + }, + { + plugins: [tsPlugin], + // extends: ['plugin:@typescript-eslint/recommended', 'eslint:recommended'], + rules: { + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-var-requires': 'off', + indent: ['error', 4, { SwitchCase: 1 }], + 'newline-per-chained-call': ['error', { ignoreChainWithDepth: 2 }], + 'no-useless-escape': 'off', + }, + }, +]; + +export default config; diff --git a/package.json b/package.json index 379f7cd..140bc56 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "bugs": { "url": "https://github.com/permafrost-dev/alpinejs-ray/issues" }, + "type": "commonjs", "main": "./dist/index.cjs.js", "module": "./dist/index.esm.mjs", "jsdelivr": "./dist/standalone.min.js", @@ -63,10 +64,10 @@ "@types/jest": "^29.2.4", "@rollup/plugin-terser": "^0.4", "@types/node": "^20.2.1", - "@typescript-eslint/eslint-plugin": "^5.14.0", - "@typescript-eslint/parser": "^5.14.0", + "@typescript-eslint/eslint-plugin": "^8.13.0", + "@typescript-eslint/parser": "^8.13.0", "concurrently": "^8.0.1", - "eslint": "^8.10.0", + "eslint": "^9.14.0", "husky": "^8.0.1", "is-ci": "^3.0.1", "jest": "^29.3.1", @@ -74,7 +75,7 @@ "prettier": "^2.5.1", "rollup": "^3.3.0", "ts-jest": "^29.0.3", - "typescript": "^5.0" + "typescript": "^5.6.0" }, "dependencies": { "minimatch": "^5.0.1", From b86b24441844fe3aa4f1d44cb8565f17982815bf Mon Sep 17 00:00:00 2001 From: Patrick Organ Date: Sun, 10 Nov 2024 15:12:48 -0500 Subject: [PATCH 02/20] upgrade deps --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 140bc56..16d3992 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "prepare": "is-ci || husky install" }, "lint-staged": { - "*.{js,ts}": [ + "*.{js,ts,mjs,cjs,mts}": [ "./node_modules/.bin/prettier --config prettier.config.js --write", "./node_modules/.bin/eslint --fix" ], @@ -68,14 +68,14 @@ "@typescript-eslint/parser": "^8.13.0", "concurrently": "^8.0.1", "eslint": "^9.14.0", - "husky": "^8.0.1", + "husky": "^9.1.6", "is-ci": "^3.0.1", "jest": "^29.3.1", - "lint-staged": "^13.0.0", - "prettier": "^2.5.1", + "lint-staged": "^15.2.10", + "prettier": "^3.3.3", "rollup": "^3.3.0", "ts-jest": "^29.0.3", - "typescript": "^5.6.0" + "typescript": "^5.6.3" }, "dependencies": { "minimatch": "^5.0.1", From 3bd2ea2484ce9fa3a394cc1e87bf50183fec0129 Mon Sep 17 00:00:00 2001 From: Patrick Organ Date: Sun, 10 Nov 2024 15:16:13 -0500 Subject: [PATCH 03/20] upgrades, install vite, vitest --- eslint.config.mjs | 10 ++++++---- package.json | 9 ++++++--- prettier.config.js | 2 +- vite.config.js | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 vite.config.js diff --git a/eslint.config.mjs b/eslint.config.mjs index 2dae642..2dfc361 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,15 +1,15 @@ import tsParser from '@typescript-eslint/parser'; import tsPlugin from '@typescript-eslint/eslint-plugin'; -import globals from "globals"; +import globals from 'globals'; import { Linter } from 'eslint'; /** @type {Linter.Config} */ const config = [ { - files: ["src/**/*.js", "src/**/*.ts"], + files: ['src/**/*.js', 'src/**/*.ts'], languageOptions: { ecmaVersion: 2022, - sourceType: "script", + sourceType: 'script', parser: tsParser, parserOptions: { ecmaVersion: 2020, @@ -22,7 +22,9 @@ const config = [ }, }, { - plugins: [tsPlugin], + plugins: { + ts: tsPlugin, + }, // extends: ['plugin:@typescript-eslint/recommended', 'eslint:recommended'], rules: { '@typescript-eslint/ban-ts-comment': 'off', diff --git a/package.json b/package.json index 16d3992..3a71d1f 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "bugs": { "url": "https://github.com/permafrost-dev/alpinejs-ray/issues" }, - "type": "commonjs", + "type": "module", "main": "./dist/index.cjs.js", "module": "./dist/index.esm.mjs", "jsdelivr": "./dist/standalone.min.js", @@ -60,12 +60,13 @@ "@rollup/plugin-commonjs": "^24.0.0", "@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-replace": "^5.0.1", + "@rollup/plugin-terser": "^0.4", "@rollup/plugin-typescript": "^10.0.1", "@types/jest": "^29.2.4", - "@rollup/plugin-terser": "^0.4", "@types/node": "^20.2.1", "@typescript-eslint/eslint-plugin": "^8.13.0", "@typescript-eslint/parser": "^8.13.0", + "@vitest/coverage-v8": "^2.1.4", "concurrently": "^8.0.1", "eslint": "^9.14.0", "husky": "^9.1.6", @@ -75,7 +76,9 @@ "prettier": "^3.3.3", "rollup": "^3.3.0", "ts-jest": "^29.0.3", - "typescript": "^5.6.3" + "typescript": "^5.6.3", + "vite": "^5.4.10", + "vitest": "^2.1.4" }, "dependencies": { "minimatch": "^5.0.1", diff --git a/prettier.config.js b/prettier.config.js index 66a716d..d106252 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -19,7 +19,7 @@ const overrides = [ }, ]; -module.exports = { +export default { arrowParens: 'avoid', bracketSameLine: true, bracketSpacing: true, diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..c7b03d5 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,33 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + name: 'AlpineRay', + globals: true, + passWithNoTests: true, + watch: false, + environment: 'jsdom', + alias: { + '@/': new URL('./src/', import.meta.url).pathname, + }, + coverage: { + all: true, + include: ['src/**/*.ts'], + exclude: ['src/index.ts', 'src/index-standalone.ts'], + reporter: [['text'], ['json', { file: 'coverage.json' }]], + }, + include: ['tests/**/*.ts', 'tests/**/*.js', 'tests/*.ts'], + reporters: ['default', process.env.CI ? 'github-actions' : 'verbose'], + }, + build: { + rollupOptions: { + treeshake: true, + }, + }, + server: { + watch: { + usePolling: true, + ignored: ['**/node_modules/**', '**/dist/**', './coverage/**', '**/.git/**'], + }, + }, +}); From a2f82566b1cb138506ebd43e34538b4b34ce29b5 Mon Sep 17 00:00:00 2001 From: Patrick Organ Date: Sun, 10 Nov 2024 15:48:40 -0500 Subject: [PATCH 04/20] upgrades, wip --- .gitignore | 1 + jest.config.js | 32 ----------------- package.json | 5 +-- .../AlpineRayMagicMethod.test.ts.snap | 2 +- vite.config.js | 36 +++++++++++++++++++ 5 files changed, 39 insertions(+), 37 deletions(-) delete mode 100644 jest.config.js diff --git a/.gitignore b/.gitignore index 3db9af7..39dd1c5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /.vscode /node_modules /dist +/dist-temp* /coverage package-lock.json diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index 38ce3fa..0000000 --- a/jest.config.js +++ /dev/null @@ -1,32 +0,0 @@ -const { pathsToModuleNameMapper } = require('ts-jest'); - -const tsConfigPaths = { - '@/*': ['src/*'], - '@tests/*': ['tests/*'], -}; - -module.exports = { - preset: 'ts-jest/presets/js-with-ts', - testEnvironment: 'node', - transform: { - '^.+\\.tsx?$': 'ts-jest', - }, - testRegex: '(/__test__/.*|/tests/.*|(\\.|/)(test|spec))\\.[tj]sx?$', - testPathIgnorePatterns: ['/node_modules/', '/dist/', '/build/', '/coverage/', '/tests/TestClasses/', '/tests/TestData/'], - moduleFileExtensions: ['js', 'ts'], - moduleNameMapper: pathsToModuleNameMapper(tsConfigPaths, { prefix: `${__dirname}/` }), - - coverageDirectory: './coverage', - coverageReporters: ['html-spa', 'text'], - collectCoverageFrom: [ - 'src/*.js', - 'src/*.ts', - 'src/**/*.js', - 'src/**/*.ts', - '!**/build/**', - '!**/dist/**', - '!**/node_modules/**', - '!**/tests/**', - '!**/vendor/**', - ], -}; diff --git a/package.json b/package.json index 3a71d1f..e375cee 100644 --- a/package.json +++ b/package.json @@ -62,8 +62,7 @@ "@rollup/plugin-replace": "^5.0.1", "@rollup/plugin-terser": "^0.4", "@rollup/plugin-typescript": "^10.0.1", - "@types/jest": "^29.2.4", - "@types/node": "^20.2.1", + "@types/node": "^22.9.0", "@typescript-eslint/eslint-plugin": "^8.13.0", "@typescript-eslint/parser": "^8.13.0", "@vitest/coverage-v8": "^2.1.4", @@ -71,11 +70,9 @@ "eslint": "^9.14.0", "husky": "^9.1.6", "is-ci": "^3.0.1", - "jest": "^29.3.1", "lint-staged": "^15.2.10", "prettier": "^3.3.3", "rollup": "^3.3.0", - "ts-jest": "^29.0.3", "typescript": "^5.6.3", "vite": "^5.4.10", "vitest": "^2.1.4" diff --git a/tests/__snapshots__/AlpineRayMagicMethod.test.ts.snap b/tests/__snapshots__/AlpineRayMagicMethod.test.ts.snap index 3b9063b..1cc74ac 100644 --- a/tests/__snapshots__/AlpineRayMagicMethod.test.ts.snap +++ b/tests/__snapshots__/AlpineRayMagicMethod.test.ts.snap @@ -1,3 +1,3 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`logs custom component events 1`] = `[]`; diff --git a/vite.config.js b/vite.config.js index c7b03d5..1ee57b3 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,5 +1,14 @@ +import { resolve } from 'node:path'; import { defineConfig } from 'vitest/config'; +function generateFilenameFromFormat(format) { + if (format === 'cjs') { + return 'alpine-ray.cjs'; + } + + return `alpine-ray.${format}.js`; +} + export default defineConfig({ test: { name: 'AlpineRay', @@ -19,8 +28,35 @@ export default defineConfig({ include: ['tests/**/*.ts', 'tests/**/*.js', 'tests/*.ts'], reporters: ['default', process.env.CI ? 'github-actions' : 'verbose'], }, + resolve: { + alias: { + '@': resolve('.', 'src'), + }, + }, build: { + lib: { + entry: 'src/index.ts', + name: 'AlpineRay', + formats: ['es', 'cjs', 'umd'], + fileName: format => generateFilenameFromFormat(format), + }, + outDir: 'dist-temp-2', + minify: true, rollupOptions: { + external: [ + 'axios', + 'dayjs', + 'crypto', + 'object-hash', + 'stopwatch-node', + 'md5', + 'node-ray', + 'node-ray/web', + '@permafrost-dev/pretty-format', + 'stacktrace-js', + 'xml-formatter', + 'uuid', + ], treeshake: true, }, }, From 41c9aa764cf8c353cb047382bc641e764665459e Mon Sep 17 00:00:00 2001 From: Patrick Organ Date: Mon, 11 Nov 2024 01:17:59 -0500 Subject: [PATCH 05/20] tooling upgrades, remove rollup and use vite for builds --- .npmignore | 22 ++++++------ .prettierignore | 10 +++--- LICENSE | 2 +- package.json | 46 +++++++++++-------------- rollup.config.mjs | 55 ----------------------------- rollup.standalone-config.mjs | 54 ----------------------------- vite.config.js | 67 +++++++++++++++++++++--------------- vite.config.standalone.js | 26 ++++++++++++++ 8 files changed, 104 insertions(+), 178 deletions(-) delete mode 100644 rollup.config.mjs delete mode 100644 rollup.standalone-config.mjs create mode 100644 vite.config.standalone.js diff --git a/.npmignore b/.npmignore index a50a8c9..967f7e3 100644 --- a/.npmignore +++ b/.npmignore @@ -1,28 +1,28 @@ +/.coverage /.git /.github +/.husky /.vscode /build /coverage /node_modules /src /tests +/dist-temp* .editorconfig .eslintrc.js .gitattributes +.markdownlint.json .prettierignore .prettierrc -jest.config.js +bun.lockb +eslint.config.js package-lock.json +prettier.config.js SECURITY.md tsconfig.json -*.sh -_*.txt +vite.config.js +vite.config.standalone.js *.ignored -rollup.vue?-config.js -examples -rollup.*.js -.markdownlint.json -/.husky -prettier.config.js -rollup.config.mjs -rollup.standalone-config.mjs +*.log +*.sh diff --git a/.prettierignore b/.prettierignore index 10706c5..f503ebf 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,7 +1,9 @@ -node_modules/* -dist/* +.coverage/* build/* -*.yml -*.yaml +dist-temp* +dist/* +node_modules/* *.ignore *.ignored +*.yaml +*.yml diff --git a/LICENSE b/LICENSE index 455d443..c63a11a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ The MIT License (MIT) -Copyright © 2021 Permafrost Development +Copyright © 2024 Permafrost Software Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/package.json b/package.json index e375cee..ab1d45c 100644 --- a/package.json +++ b/package.json @@ -13,26 +13,27 @@ "url": "https://github.com/permafrost-dev/alpinejs-ray/issues" }, "type": "module", - "main": "./dist/index.cjs.js", - "module": "./dist/index.esm.mjs", - "jsdelivr": "./dist/standalone.min.js", - "unpkg": "./dist/standalone.min.js", + "main": "./dist/lib/index.js", + "module": "./dist/lib/index.js", + "jsdelivr": "./dist/cdn/standalone.umd.js", + "unpkg": "./dist/cdn/standalone.umd.js", "exports": { ".": { - "import": "./dist/index.esm.mjs", - "require": "./dist/index.cjs.js" + "default": "./dist/lib/index.js", + "import": "./dist/lib/index.js", + "require": "./dist/lib/index.cjs" } }, "scripts": { - "test": "./node_modules/.bin/jest tests --verbose", - "test:coverage": "./node_modules/.bin/jest tests --coverage", - "fmt": "./node_modules/.bin/prettier --config prettier.config.js --write 'src/**/*.{js,ts,json}' 'tests/**/*.{js,ts,json}' './*.js'", - "lint": "./node_modules/.bin/eslint --ext ts,js src/", - "lint:fix": "./node_modules/.bin/eslint --ext ts,js --fix src/", - "lint:staged": "./node_modules/.bin/lint-staged", + "test": "vitest tests --verbose", + "test:coverage": "vitest tests --coverage", + "fmt": "prettier --config prettier.config.js --write 'src/**/*.{js,ts,json}' 'tests/**/*.{js,ts,json}' './*.js'", + "lint": "eslint --ext ts,js src/", + "lint:fix": "eslint --ext ts,js --fix src/", + "lint:staged": "lint-staged", "fix": "npm run fmt && npm run lint:fix", - "build:module": "rollup --config rollup.config.mjs", - "build:standalone": "rollup --config rollup.standalone-config.mjs", + "build:module": "vite build", + "build:standalone": "vite build --config vite.config.standalone.js", "build:all": "concurrently npm:build:module npm:build:standalone", "preversion": "npm run test", "postversion": "npm run build:all", @@ -40,39 +41,34 @@ }, "lint-staged": { "*.{js,ts,mjs,cjs,mts}": [ - "./node_modules/.bin/prettier --config prettier.config.js --write", - "./node_modules/.bin/eslint --fix" + "prettier --config prettier.config.js --write", + "eslint --fix" ], "*.{json,css,scss,gql}": [ - "./node_modules/.bin/prettier --config prettier.config.js --write" + "prettier --config prettier.config.js --write" ], "*.{yaml,yml}": [ - "./node_modules/.bin/prettier --config prettier.config.js --tab-width 2 --write" + "prettier --config prettier.config.js --tab-width 2 --write" ] }, "keywords": [ "alpinejs", "ray", + "spatie", "debug", "permafrost" ], "devDependencies": { - "@rollup/plugin-commonjs": "^24.0.0", - "@rollup/plugin-node-resolve": "^15.0.1", - "@rollup/plugin-replace": "^5.0.1", - "@rollup/plugin-terser": "^0.4", - "@rollup/plugin-typescript": "^10.0.1", "@types/node": "^22.9.0", "@typescript-eslint/eslint-plugin": "^8.13.0", "@typescript-eslint/parser": "^8.13.0", "@vitest/coverage-v8": "^2.1.4", - "concurrently": "^8.0.1", + "concurrently": "^9.1.0", "eslint": "^9.14.0", "husky": "^9.1.6", "is-ci": "^3.0.1", "lint-staged": "^15.2.10", "prettier": "^3.3.3", - "rollup": "^3.3.0", "typescript": "^5.6.3", "vite": "^5.4.10", "vitest": "^2.1.4" diff --git a/rollup.config.mjs b/rollup.config.mjs deleted file mode 100644 index fe4dcb1..0000000 --- a/rollup.config.mjs +++ /dev/null @@ -1,55 +0,0 @@ -import commonjs from '@rollup/plugin-commonjs'; -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import replace from '@rollup/plugin-replace'; -import typescript from '@rollup/plugin-typescript'; -import packageJson from './package.json' with { type: "json" }; - -const options = { - sourceMapsEnabled: false, - minified: false, -}; - -export default { - input: `src/index.ts`, - output: [ - { - file: `dist/index.cjs.js`, - format: 'cjs', - sourcemap: options.sourceMapsEnabled, - exports: 'named', - plugins: [], - }, - { - file: `dist/index.esm.mjs`, - format: 'esm', - sourcemap: options.sourceMapsEnabled, - plugins: [], - }, - ], - plugins: [ - replace({ - values: { - __BUILD_DATE__: () => new Date().toISOString(), - __BUILD_VERSION__: () => packageJson.version, - }, - preventAssignment: true, - }), - nodeResolve(), - commonjs(), - typescript(), - ], - external: [ - 'axios', - 'dayjs', - 'crypto', - 'object-hash', - 'stopwatch-node', - 'md5', - 'node-ray', - 'node-ray/web', - '@permafrost-dev/pretty-format', - 'stacktrace-js', - 'xml-formatter', - 'uuid', - ], -}; diff --git a/rollup.standalone-config.mjs b/rollup.standalone-config.mjs deleted file mode 100644 index 9818327..0000000 --- a/rollup.standalone-config.mjs +++ /dev/null @@ -1,54 +0,0 @@ -import commonjs from '@rollup/plugin-commonjs'; -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import replace from '@rollup/plugin-replace'; -import typescript from '@rollup/plugin-typescript'; -// import { terser } from '@rollup/plugin-terser'; -import packageJson from './package.json' with { type: "json" }; - -const options = { - sourceMapsEnabled: true, - minified: true, -}; - -const outputEntry = { - file: `dist/standalone.js`, - format: 'umd', - //name: 'AlpineRay', - sourcemap: options.sourceMapsEnabled, - //exports: 'named', - plugins: [], - globals: { - axios: 'axios', - }, -}; - -function makeEntryMinified(entry) { - const result = Object.assign({}, entry); - - result['file'] = result['file'].replace(/\.js$/, '.min.js'); - result['plugins'] = []; - - return Object.freeze(result); -} - -function getEntryObject(entry) { - return Object.freeze(Object.assign({}, entry)); -} - -export default { - input: `src/index-standalone.ts`, - output: [getEntryObject(outputEntry), makeEntryMinified(outputEntry)], - plugins: [ - replace({ - values: { - __BUILD_DATE__: () => new Date().toISOString(), - __BUILD_VERSION__: () => packageJson.version, - }, - preventAssignment: true, - }), - nodeResolve(), - commonjs(), - typescript(), - ], - external: ['axios'], -}; diff --git a/vite.config.js b/vite.config.js index 1ee57b3..484265f 100644 --- a/vite.config.js +++ b/vite.config.js @@ -3,46 +3,34 @@ import { defineConfig } from 'vitest/config'; function generateFilenameFromFormat(format) { if (format === 'cjs') { - return 'alpine-ray.cjs'; + return 'index.cjs'; } - return `alpine-ray.${format}.js`; + if (format === 'es') { + return 'index.js'; + } + + return `index.${format}.js`; } export default defineConfig({ - test: { - name: 'AlpineRay', - globals: true, - passWithNoTests: true, - watch: false, - environment: 'jsdom', - alias: { - '@/': new URL('./src/', import.meta.url).pathname, - }, - coverage: { - all: true, - include: ['src/**/*.ts'], - exclude: ['src/index.ts', 'src/index-standalone.ts'], - reporter: [['text'], ['json', { file: 'coverage.json' }]], - }, - include: ['tests/**/*.ts', 'tests/**/*.js', 'tests/*.ts'], - reporters: ['default', process.env.CI ? 'github-actions' : 'verbose'], - }, - resolve: { - alias: { - '@': resolve('.', 'src'), - }, - }, build: { + outDir: 'dist-temp-2/lib', + sourcemap: true, + minify: true, lib: { entry: 'src/index.ts', name: 'AlpineRay', - formats: ['es', 'cjs', 'umd'], + formats: ['es', 'cjs'], fileName: format => generateFilenameFromFormat(format), }, - outDir: 'dist-temp-2', - minify: true, rollupOptions: { + output: { + globals: { + axios: 'axios', + dayjs: 'dayjs', + }, + }, external: [ 'axios', 'dayjs', @@ -60,10 +48,33 @@ export default defineConfig({ treeshake: true, }, }, + resolve: { + alias: { + '@': resolve('.', 'src'), + }, + }, server: { watch: { usePolling: true, ignored: ['**/node_modules/**', '**/dist/**', './coverage/**', '**/.git/**'], }, }, + test: { + name: 'AlpineRay', + globals: true, + passWithNoTests: true, + watch: false, + environment: 'jsdom', + alias: { + '@/': new URL('./src/', import.meta.url).pathname, + }, + coverage: { + all: true, + include: ['src/**/*.ts'], + exclude: ['src/index.ts', 'src/index-standalone.ts'], + reporter: [['text'], ['json', { file: 'coverage.json' }]], + }, + include: ['tests/**/*.ts', 'tests/**/*.js', 'tests/*.ts'], + reporters: ['default', process.env.CI ? 'github-actions' : 'verbose'], + }, }); diff --git a/vite.config.standalone.js b/vite.config.standalone.js new file mode 100644 index 0000000..4e80a98 --- /dev/null +++ b/vite.config.standalone.js @@ -0,0 +1,26 @@ +import { resolve } from 'node:path'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + build: { + outDir: 'dist-temp-2/cdn', + sourcemap: true, + minify: true, + lib: { + entry: 'src/index-standalone.ts', + name: 'AlpineRay', + formats: ['umd'], + fileName: format => `standalone.${format}.js`, + }, + rollupOptions: { + external: ['axios'], + output: { globals: { axios: 'axios' } }, + treeshake: true, + }, + }, + resolve: { + alias: { + '@': resolve('.', 'src'), + }, + }, +}); From b88519a80351a260091d91a4bba6b1f7e84ad7dd Mon Sep 17 00:00:00 2001 From: Patrick Organ Date: Mon, 11 Nov 2024 01:22:57 -0500 Subject: [PATCH 06/20] upgrade tests workflow --- .github/workflows/run-tests.yml | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index b529592..2ba9035 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -5,17 +5,16 @@ on: branches: - main pull_request: - branches: - - main + pull_request_target: + jobs: tests: runs-on: ubuntu-latest - strategy: fail-fast: true matrix: - node-version: [14, 16] + node-version: [20, 22] steps: - name: Checkout repository @@ -28,31 +27,18 @@ jobs: with: node-version: ${{ matrix.node-version }} - - name: Get yarn cache directory path - id: npm-cache-dir-path - run: echo "::set-output name=dir::$(npm config get cache)" - - - name: Cache dependencies - uses: actions/cache@v4.1.2 - id: npm-cache + - uses: oven-sh/setup-bun@v2 with: - path: ${{ steps.npm-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-node-${{ matrix.node-version }}-npm-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node-${{ matrix.node-version }}-npm- + bun-version: latest - name: Install dependencies - run: npm install - - - name: Run the tests - run: npm run test + run: bun install - name: Run the tests with coverage - run: npm run test -- --coverage --coverageReporters json + run: bun run test:coverage - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4.6.0 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} - files: coverage/coverage-final.json - + files: coverage/coverage.json From 23f6e6257f0e682d7ea65102092fabee7f305405 Mon Sep 17 00:00:00 2001 From: Patrick Organ Date: Mon, 11 Nov 2024 01:27:22 -0500 Subject: [PATCH 07/20] config updates, upgrades/cleanup to workflows --- .github/FUNDING.yml | 1 + .github/dependabot.yml | 4 +-- .github/workflows/codeql-analysis.yml | 39 ++------------------- .github/workflows/dependabot-auto-merge.yml | 16 ++++----- 4 files changed, 13 insertions(+), 47 deletions(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 2b03105..5433e0b 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,2 @@ github: permafrost-dev +custom: https://permafrost.dev/open-source diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e5bcc00..8c7c556 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,14 +4,14 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "daily" + interval: "weekly" labels: - "dependencies" - package-ecosystem: "npm" directory: "/" schedule: - interval: "daily" + interval: "weekly" allow: - dependency-type: "direct" commit-message: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d961a66..559e093 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,22 +1,8 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# name: "CodeQL" on: - #push: - # branches: [ main ] - #pull_request: - # # The branches below must be a subset of the branches above - # branches: [ main ] + push: + branches: [ main ] schedule: - cron: '24 2 * * 1' @@ -29,39 +15,18 @@ jobs: fail-fast: false matrix: language: [ 'javascript' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository uses: actions/checkout@v4 - # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v3 - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml index 7dcca24..cafca6f 100644 --- a/.github/workflows/dependabot-auto-merge.yml +++ b/.github/workflows/dependabot-auto-merge.yml @@ -10,23 +10,23 @@ jobs: runs-on: ubuntu-latest if: ${{ github.actor == 'dependabot[bot]' }} steps: - + - name: Dependabot metadata id: metadata uses: dependabot/fetch-metadata@v2 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - - - name: Auto-merge Dependabot PRs for semver-minor updates - if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}} + + - name: Dependabot auto-merge minor & patch updates + if: ${{steps.metadata.outputs.update-type != 'version-update:semver-major'}} run: gh pr merge --auto --merge "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - - - name: Auto-merge Dependabot PRs for semver-patch updates - if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} + + - name: Dependabot auto-merge actions major updates (if compat >= 90%) + if: ${{steps.metadata.outputs.package-ecosystem == 'github_actions' && steps.metadata.outputs.update-type == 'version-update:semver-major' && steps.metadata.outputs.compatibility-score >= 90}} run: gh pr merge --auto --merge "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + GH_TOKEN: ${{secrets.GITHUB_TOKEN}} From b0964197ad7126d7663a91dba4c4715d485717d3 Mon Sep 17 00:00:00 2001 From: Patrick Organ Date: Mon, 11 Nov 2024 01:30:04 -0500 Subject: [PATCH 08/20] code cleanup --- tsconfig.json | 85 +++++++++++---------------------------------------- 1 file changed, 18 insertions(+), 67 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index f7edcaf..7d0da61 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,73 +1,24 @@ { "compilerOptions": { - /* Visit https://aka.ms/tsconfig.json to read more about this file */ - - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, - "module": "esnext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, - "lib": ["esnext"] /* Specify library files to be included in the compilation. */, - "allowJs": true /* Allow javascript files to be compiled. */, - "checkJs": false, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - // "declaration": true /* Generates corresponding '.d.ts' file. */, - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - // "outDir": "./dist1" /* Redirect output structure to the directory. */, - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - "removeComments": true /* Do not emit comments to output. */, - // "noEmit": true, /* Do not emit outputs. */ - "importHelpers": true /* Import emit helpers from 'tslib'. */, - "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true /* Enable all strict type-checking options. */, - "noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ - - /* Module Resolution Options */ - "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, - "baseUrl": "./" /* Base directory to resolve non-absolute module names. */, + "target": "esnext", + "module": "esnext", + "lib": ["esnext"], + "allowJs": true, + "checkJs": false, + "removeComments": true, + "importHelpers": true, + "downlevelIteration": true, + "strict": true, + "noImplicitAny": false, + "moduleResolution": "Bundler", + "baseUrl": "./", "paths": { "@/*": ["src/*"] - } /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */, - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": false, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - //"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - //"emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - - /* Advanced Options */ - "skipLibCheck": true /* Skip type checking of declaration files. */, - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "allowUmdGlobalAccess": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true } - //"include": ["./src/*.ts", "./src/**/*.ts", "./src/**/*.js"] } From 89e18714b6c23e9d1ebd283b0bfd507d7f7f7f64 Mon Sep 17 00:00:00 2001 From: Patrick Organ Date: Mon, 11 Nov 2024 01:30:55 -0500 Subject: [PATCH 09/20] wip --- vite.config.js | 2 +- vite.config.standalone.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vite.config.js b/vite.config.js index 484265f..6efc2d3 100644 --- a/vite.config.js +++ b/vite.config.js @@ -15,7 +15,7 @@ function generateFilenameFromFormat(format) { export default defineConfig({ build: { - outDir: 'dist-temp-2/lib', + outDir: 'dist/lib', sourcemap: true, minify: true, lib: { diff --git a/vite.config.standalone.js b/vite.config.standalone.js index 4e80a98..9ee69b4 100644 --- a/vite.config.standalone.js +++ b/vite.config.standalone.js @@ -3,7 +3,7 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ build: { - outDir: 'dist-temp-2/cdn', + outDir: 'dist/cdn', sourcemap: true, minify: true, lib: { From 62681504b671463c99a88c77482107736f851bea Mon Sep 17 00:00:00 2001 From: Patrick Organ Date: Mon, 11 Nov 2024 01:31:40 -0500 Subject: [PATCH 10/20] rename eslint config file --- eslint.config.mjs => eslint.config.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename eslint.config.mjs => eslint.config.js (100%) diff --git a/eslint.config.mjs b/eslint.config.js similarity index 100% rename from eslint.config.mjs rename to eslint.config.js From d7403051964396bcd9e6e77ef40d2030ad4c770d Mon Sep 17 00:00:00 2001 From: Patrick Organ Date: Mon, 11 Nov 2024 01:33:38 -0500 Subject: [PATCH 11/20] update codecov config --- .github/codecov.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/codecov.yml b/.github/codecov.yml index 7259276..37b0e91 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -1,6 +1,6 @@ codecov: branch: main - require_ci_to_pass: yes + require_ci_to_pass: true coverage: status: @@ -8,14 +8,13 @@ coverage: default: informational: true target: auto - # this allows a 10% drop from the previous base commit coverage threshold: 20% patch: default: informational: true precision: 2 round: up - range: "40...80" + range: "70...100" parsers: javascript: @@ -33,12 +32,13 @@ comment: require_changes: true ignore: + - ".husky/" - "build/" - "dist/" + - "coverage/" - "src/index.ts" - "src/index-standalone.ts" - - ".eslintrc.js" - - "jest.config.js" + - "eslint.config.js" - "prettier.config.js" - - "rollup.config.js" - - "rollup.standalone-config.js" + - "vite.config.js" + - "vite.config.standalone.js" From 8780d9a0b2f8dee3ebfe1fba1e422fd2a4717538 Mon Sep 17 00:00:00 2001 From: Patrick Organ Date: Tue, 12 Nov 2024 06:59:21 -0500 Subject: [PATCH 12/20] update node-ray and minimatch deps to latest --- .husky/pre-commit | 1 - package.json | 8 ++++---- src/AlpineRay.ts | 4 +--- src/AlpineRayMagicMethod.ts | 21 +++++++-------------- tests/AlpineRay.test.ts | 7 ------- 5 files changed, 12 insertions(+), 29 deletions(-) delete mode 100644 tests/AlpineRay.test.ts diff --git a/.husky/pre-commit b/.husky/pre-commit index 3199e8e..015c708 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,3 @@ #!/bin/sh -. "$(dirname "$0")/_/husky.sh" npm run lint:staged diff --git a/package.json b/package.json index ab1d45c..b2b3966 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ } }, "scripts": { - "test": "vitest tests --verbose", + "test": "vitest tests", "test:coverage": "vitest tests --coverage", "fmt": "prettier --config prettier.config.js --write 'src/**/*.{js,ts,json}' 'tests/**/*.{js,ts,json}' './*.js'", "lint": "eslint --ext ts,js src/", @@ -37,7 +37,7 @@ "build:all": "concurrently npm:build:module npm:build:standalone", "preversion": "npm run test", "postversion": "npm run build:all", - "prepare": "is-ci || husky install" + "prepare": "is-ci || husky" }, "lint-staged": { "*.{js,ts,mjs,cjs,mts}": [ @@ -74,7 +74,7 @@ "vitest": "^2.1.4" }, "dependencies": { - "minimatch": "^5.0.1", - "node-ray": "^1.18.0" + "minimatch": "^10.0.1", + "node-ray": "^2.1.2" } } diff --git a/src/AlpineRay.ts b/src/AlpineRay.ts index e273ea9..0694b3f 100644 --- a/src/AlpineRay.ts +++ b/src/AlpineRay.ts @@ -1,11 +1,9 @@ -/* eslint-disable no-undef */ + import { Ray } from 'node-ray/web'; import { getWindow } from '@/lib/utils'; export class AlpineRay extends Ray { - public static $version = '__BUILD_VERSION__'; - public rayInstance: any; public trackRays: Record = { store: {}, diff --git a/src/AlpineRayMagicMethod.ts b/src/AlpineRayMagicMethod.ts index fa42bb0..78b14a7 100644 --- a/src/AlpineRayMagicMethod.ts +++ b/src/AlpineRayMagicMethod.ts @@ -1,14 +1,7 @@ -import {ray} from '@/AlpineRay'; -import {AlpineRayConfig, getAlpineRayConfig} from '@/AlpineRayConfig'; -import { - checkForAxios, - encodeHtmlEntities, - filterObjectKeys, - findParentComponent, - getWindow, - highlightHtmlMarkup -} from '@/lib/utils'; -import minimatch from 'minimatch'; +import { ray } from '@/AlpineRay'; +import { AlpineRayConfig, getAlpineRayConfig } from '@/AlpineRayConfig'; +import { checkForAxios, encodeHtmlEntities, filterObjectKeys, findParentComponent, getWindow, highlightHtmlMarkup } from '@/lib/utils'; +import { minimatch } from 'minimatch'; function getMatches(patterns: string[], values: string[]) { const result: string[] = []; @@ -78,7 +71,7 @@ const AlpineRayMagicMethod = { if (errorEvent.error || errorEvent.reason) { const data = errorEvent.reason || errorEvent.error; - const {el, expression} = data; + const { el, expression } = data; const parentComponent = findParentComponent(el); // component and parent components are not alpine components, so do nothing @@ -92,7 +85,7 @@ const AlpineRayMagicMethod = { `${encodeHtmlEntities(expression)}`, ); - const componentData = parentComponent.__x ?? {$data: {}}; + const componentData = parentComponent.__x ?? { $data: {} }; rayInstance().table( { @@ -132,7 +125,7 @@ const AlpineRayMagicMethod = { checkForAxios(window); - Alpine.directive('ray', (el, {expression}, {evaluateLater, effect}) => { + Alpine.directive('ray', (el, { expression }, { evaluateLater, effect }) => { const result = evaluateLater(expression); effect(() => { diff --git a/tests/AlpineRay.test.ts b/tests/AlpineRay.test.ts deleted file mode 100644 index 5b5dfac..0000000 --- a/tests/AlpineRay.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* eslint-disable no-undef */ - -import { AlpineRay } from '../src/AlpineRay'; - -it('has a version number', () => { - expect(AlpineRay.$version).toBe('__BUILD_VERSION__'); -}); From 5b807c8cf57f505b18d90f40dd1ed8aed90eb3a1 Mon Sep 17 00:00:00 2001 From: Patrick Organ Date: Tue, 12 Nov 2024 07:00:22 -0500 Subject: [PATCH 13/20] update prettier/eslint configs --- eslint.config.js | 1 + prettier.config.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/eslint.config.js b/eslint.config.js index 2dfc361..9a6a29b 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -34,6 +34,7 @@ const config = [ '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-var-requires': 'off', indent: ['error', 4, { SwitchCase: 1 }], + 'max-len': ['warn', { code: 160, ignoreUrls: true }], 'newline-per-chained-call': ['error', { ignoreChainWithDepth: 2 }], 'no-useless-escape': 'off', }, diff --git a/prettier.config.js b/prettier.config.js index d106252..4b51bbf 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -27,7 +27,7 @@ export default { insertPragma: false, jsxSingleQuote: false, overrides, - printWidth: 140, + printWidth: 160, proseWrap: 'preserve', quoteProps: 'as-needed', requirePragma: false, From b465ce68391b7fcc5b650b2d57225d63aa513b59 Mon Sep 17 00:00:00 2001 From: Patrick Organ Date: Tue, 12 Nov 2024 12:37:21 -0500 Subject: [PATCH 14/20] refactoring, adding tests, code cleanup --- package.json | 2 + src/globals.d.ts | 11 ++ src/lib/utils.ts | 63 ++++--- tests/fakes/FakeHTMLElement.ts | 36 ++++ tests/lib/__snapshots__/utils.test.ts.snap | 5 + tests/lib/utils.test.ts | 185 ++++++++++++++++++++- 6 files changed, 268 insertions(+), 34 deletions(-) create mode 100644 src/globals.d.ts create mode 100644 tests/fakes/FakeHTMLElement.ts create mode 100644 tests/lib/__snapshots__/utils.test.ts.snap diff --git a/package.json b/package.json index b2b3966..4d6a2c4 100644 --- a/package.json +++ b/package.json @@ -59,10 +59,12 @@ "permafrost" ], "devDependencies": { + "@types/alpinejs": "^3.13.10", "@types/node": "^22.9.0", "@typescript-eslint/eslint-plugin": "^8.13.0", "@typescript-eslint/parser": "^8.13.0", "@vitest/coverage-v8": "^2.1.4", + "alpinejs": "^3.14.3", "concurrently": "^9.1.0", "eslint": "^9.14.0", "husky": "^9.1.6", diff --git a/src/globals.d.ts b/src/globals.d.ts new file mode 100644 index 0000000..0f21e05 --- /dev/null +++ b/src/globals.d.ts @@ -0,0 +1,11 @@ +import axios from 'axios'; +import Alpine from 'alpinejs'; +import { Ray } from 'node-ray/dist/web'; + +declare global { + interface Window { + Alpine?: typeof Alpine; + Ray: Ray; + axios?: typeof axios; + } +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 370a5d1..9175bef 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,16 +1,16 @@ -/* eslint-disable no-undef */ - -export function getWindow() { - return globalThis; +/** + * Returns the global window object. + */ +export function getWindow(): Window { + return window || globalThis; } /** * Determines if the axios library has been installed and initialized in the current browser environment. * - * @param window */ -export const checkForAxios = (window: any = null) => { - const win: any = window ?? getWindow(); +export const checkForAxios = (window: Window | null = null) => { + const win: Window = (window ?? getWindow()) as Window; if (!win.axios) { throw new Error('[alpinejs-ray] axios is required for alpinejs-ray to function correctly.'); @@ -20,23 +20,20 @@ export const checkForAxios = (window: any = null) => { /** * Determines if the node-ray standalone library has been installed and initialized in the current browser environment. * - * @param window */ export const checkForRay = (window: any = null) => { - const win: any = window ?? getWindow(); + const win: Window = (window ?? getWindow()) as Window; - if (!win.Ray || !win.Ray.Ray || !win.Ray.ray) { + if (!win.Ray || Object.keys(win.Ray).length === 0) { throw new Error('[alpinejs-ray] node-ray is required for alpinejs-ray to function correctly.'); } }; /** * Determines if alpine.js has been installed and initialized in the current browser environment. - * - * @param window */ export const checkForAlpine = (window: any = null) => { - const win: any = window ?? getWindow(); + const win: Window = (window ?? getWindow()) as Window; if (!win.Alpine) { throw new Error('[alpinejs-ray] Alpine is required for alpinejs-ray to function correctly.'); @@ -58,25 +55,19 @@ export const checkForAlpine = (window: any = null) => { * @returns boolean */ export function isValidVersion(required: string, current: string) { - let currentVersionIdentStr = '', - requiredVersionIdentStr = ''; + const shiftedNum = n => (!n || n === '0' ? 100 : Number.parseInt(n.padEnd(3, '0')) || 100); - const requiredArray: number[] = required.split('.').map(part => 1000 + parseInt(part)); - let currentArray: number[] = current.split('.').map(part => 1000 + parseInt(part)); + const requiredArr = required.split('.').map(part => shiftedNum(part)); + const currentArr = current.split('.').map(part => shiftedNum(part)); - while (currentArray.length < requiredArray.length) { - currentArray.push(1000); + while (currentArr.length < requiredArr.length && currentArr.length < 5) { + currentArr.push(100); } - // ensure the array lengths match - currentArray = currentArray.slice(0, requiredArray.length - 1); - - for (let idx = 0; idx < currentArray.length; idx++) { - currentVersionIdentStr += currentArray[idx].toString(); - requiredVersionIdentStr += requiredArray[idx].toString(); - } + const currentLong = Number(currentArr.join('')); + const requiredLong = Number(requiredArr.join('')); - return Number(currentVersionIdentStr) >= Number(requiredVersionIdentStr); + return currentLong >= requiredLong; } export const encodeHtmlEntities = (str: string) => { @@ -136,7 +127,7 @@ export const highlightHtmlMarkup = (str: string) => { ); }; -export function getComponentName(element: any) { +export function getComponentName(element: HTMLElement) { return ( element.getAttribute('x-title') || element.getAttribute('x-id') || @@ -152,7 +143,6 @@ export function getComponentName(element: any) { ); } -// TODO: Not sure how to test this export function findWireID(wireId, window: any = null) { window = window ?? getWindow(); @@ -185,9 +175,14 @@ export function findLiveViewName(alpineEl, window: any = null) { } export function extractFunctionName(functionName) { - if (functionName.startsWith('{')) { - return; + if (!functionName) { + return ''; } + + if (functionName?.startsWith('{')) { + return ''; + } + return functionName .replace(/\(([^\)]+)\)/, '') // Handles myFunction(param) .replace('()', ''); @@ -201,9 +196,13 @@ export function findParentComponent(el: any) { counter++; e = e.parentElement; - if (e.hasAttribute('x-data')) { + if (e?.hasAttribute('x-data')) { return e; } + + if (!e) { + return null; + } } return null; diff --git a/tests/fakes/FakeHTMLElement.ts b/tests/fakes/FakeHTMLElement.ts new file mode 100644 index 0000000..101542d --- /dev/null +++ b/tests/fakes/FakeHTMLElement.ts @@ -0,0 +1,36 @@ +export class FakeHTMLElement { + attributes: Record = {}; + id: string = ''; + tagName: string = 'div'; + parentElement: FakeHTMLElement | null = null; + + constructor(attributes: Record = {}, id: string = '', tagName: string = 'div') { + this.attributes = attributes; + this.id = id; + this.tagName = tagName; + } + + getAttribute(name: string): string | null { + return this.attributes[name] || null; + } + + closest(selector: string): FakeHTMLElement | null { + return this.parentElement; + } + + hasAttribute(name: string): boolean { + return name in this.attributes; + } +} + +// generate a series of fake elements for testing, by setting the parentElement property: +export function generateFakeElements(): FakeHTMLElement { + const parent = new FakeHTMLElement({ 'x-data': 'parentData' }); + const child = new FakeHTMLElement({ 'x-data': 'childData' }); + const grandChild = new FakeHTMLElement({ 'x-data': 'grandChildData' }); + + grandChild.parentElement = child; + child.parentElement = parent; + + return grandChild; +} diff --git a/tests/lib/__snapshots__/utils.test.ts.snap b/tests/lib/__snapshots__/utils.test.ts.snap new file mode 100644 index 0000000..31320c5 --- /dev/null +++ b/tests/lib/__snapshots__/utils.test.ts.snap @@ -0,0 +1,5 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`checks that html syntax highlighting works 1`] = `"<div x-data="{
test
:1}" class="bg-red-400 p-1 rounded-md"><a @click="console.log(123);"
>Hello, world!</a></div>"`; + +exports[`encodes HTML entities correctly 1`] = `"<div class="test">Hello & welcome!</div>"`; diff --git a/tests/lib/utils.test.ts b/tests/lib/utils.test.ts index e52a670..2a02f40 100644 --- a/tests/lib/utils.test.ts +++ b/tests/lib/utils.test.ts @@ -1,6 +1,20 @@ /* eslint-disable no-undef */ - -import { checkForAlpine, checkForRay, isValidVersion } from '../../src/lib/utils'; +import { expect, it } from 'vitest'; +import { + checkForAlpine, + checkForAxios, + checkForRay, + encodeHtmlEntities, + extractFunctionName, + filterObjectKeys, + findLiveViewName, + findParentComponent, + findWireID, + getComponentName, + highlightHtmlMarkup, + isValidVersion, +} from '../../src/lib/utils'; +import { FakeHTMLElement, generateFakeElements } from '../fakes/FakeHTMLElement'; it('throws an error if node-ray is not installed', () => { let error = null; @@ -74,4 +88,171 @@ it('checks for valid version strings', () => { expect(isValidVersion('2.5.0', '3.2.1')).toBeTruthy(); expect(isValidVersion('2.5.0', 'badversion')).toBeFalsy(); expect(isValidVersion('2.5.0', '')).toBeFalsy(); + + expect(isValidVersion('1.5.0', '3.3.2')).toBeTruthy(); + expect(isValidVersion('2.5.81', '3.1.2')).toBeTruthy(); +}); + +it('checks that html syntax highlighting works', () => { + const result = highlightHtmlMarkup(''); + expect(result).toMatchSnapshot(); +}); + +it('gets the component name from various attributes', () => { + const element = new FakeHTMLElement({ 'x-title': 'test-title' }); + expect(getComponentName(element as unknown as HTMLElement)).toBe('test-title'); + + element.attributes = { 'x-id': 'test-id' }; + expect(getComponentName(element as unknown as HTMLElement)).toBe('test-id'); + + element.id = 'test-id'; + expect(getComponentName(element as unknown as HTMLElement)).toBe('test-id'); + + element.id = ''; + element.attributes = { name: 'test-name' }; + expect(getComponentName(element as unknown as HTMLElement)).toBe('test-name'); + + element.attributes = { title: 'test-title-attr' }; + expect(getComponentName(element as unknown as HTMLElement)).toBe('test-title-attr'); + + element.attributes = { 'aria-label': 'test-aria-label' }; + expect(getComponentName(element as unknown as HTMLElement)).toBe('test-aria-label'); + + element.attributes = { 'x-data': 'testFunction()' }; + expect(getComponentName(element as unknown as HTMLElement)).toBe('testFunction'); + + element.attributes = { role: 'test-role' }; + expect(getComponentName(element as unknown as HTMLElement)).toBe('test-role'); + + element.attributes = {}; + element.tagName = 'span'; + expect(getComponentName(element as unknown as HTMLElement)).toBe('span'); +}); + +it('finds the parent component with x-data attribute', () => { + // const parent = new FakeHTMLElement({ 'x-data': 'parentData' }); + // const child = new FakeHTMLElement(); + // child.parentElement = parent; + const el = generateFakeElements(); + const parent = el.parentElement; + + expect(findParentComponent(el as unknown as HTMLElement)).toBe(parent); +}); + +it('returns null if no parent component with x-data attribute is found', () => { + const child = new FakeHTMLElement(); + expect(findParentComponent(child as unknown as HTMLElement)).toBeNull(); +}); +it('throws an error if axios is not installed', () => { + let error = null; + + try { + checkForAxios({} as unknown as Window); + } catch (err: any) { + error = err; + } + + expect(error).not.toBeNull(); +}); + +it('does not throw an error if axios is installed', () => { + const win = { + axios: {}, + }; + + expect(checkForAxios(win as unknown as Window)).toBeUndefined(); +}); + +it('encodes HTML entities correctly', () => { + const str = '
Hello & welcome!
'; + + expect(encodeHtmlEntities(str)).toMatchSnapshot(); +}); + +it('filters object keys correctly', () => { + const obj = { a: 1, b: 2, c: 3 }; + const ignoreKeys = ['b']; + const filteredObj = filterObjectKeys(obj, ignoreKeys); + + expect(filteredObj).toEqual({ a: 1, c: 3 }); +}); + +it('finds wire ID correctly', () => { + const wireId = 'test-wire-id'; + const win = { + livewire: { + find: id => ({ + __instance: { + fingerprint: { + name: 'test-name', + }, + }, + }), + }, + }; + + expect(findWireID(wireId, win)).toBe('livewire:test-name'); +}); + +it('returns undefined if wire ID is not found', () => { + const wireId = 'test-wire-id'; + const win = { + livewire: { + find: id => null, + }, + }; + + expect(findWireID(wireId, win)).toBeUndefined(); +}); + +it('finds LiveView name correctly', () => { + const alpineEl = { + closest: () => ({ + dataset: { + phxView: 'test-view', + }, + }), + }; + const win = { + liveSocket: { + getViewByEl: el => ({ + name: 'test-view-name', + }), + }, + }; + + expect(findLiveViewName(alpineEl, win)).toBe('test-view-name'); +}); + +it('returns undefined if LiveView name is not found', () => { + const alpineEl = { + closest: () => null, + }; + const win = { + liveSocket: { + getViewByEl: el => null, + }, + }; + + expect(findLiveViewName(alpineEl, win)).toBeUndefined(); +}); + +it('extracts function name correctly', () => { + expect(extractFunctionName('testFunction()')).toBe('testFunction'); + expect(extractFunctionName('testFunction(param)')).toBe('testFunction'); + expect(extractFunctionName('{testFunction}')).toBe(''); + expect(extractFunctionName('')).toBe(''); +}); + +it('finds parent component with x-data attribute', () => { + const parent = new FakeHTMLElement({ 'x-data': 'parentData' }); + const child = new FakeHTMLElement(); + child.parentElement = parent; + + expect(findParentComponent(child as unknown as HTMLElement)).toBe(parent); +}); + +it('returns null if no parent component with x-data attribute is found', () => { + const child = new FakeHTMLElement(); + expect(findParentComponent(child as unknown as HTMLElement)).toBeNull(); }); From e64e7ea3bf0b8625f60824b7137bd947d37daa5e Mon Sep 17 00:00:00 2001 From: Patrick Organ Date: Tue, 12 Nov 2024 12:37:49 -0500 Subject: [PATCH 15/20] update docs --- README.md | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a5b0ed2..900e6f4 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,8 @@ The preferred way to use this package is to load it via CDN, which must be done The `axios` library must be loaded prior to loading `alpinejs-ray` and `Alpine`: ```html - - +