diff --git a/.eslintprettier.config.js b/.eslintprettier.config.js
deleted file mode 100644
index ccee73a..0000000
--- a/.eslintprettier.config.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import prettierPlugin from 'eslint-plugin-prettier'
-import eslintConfig from './eslint.config.mjs'
-
-export default [
- ...eslintConfig,
- {
- plugins: {
- prettier: prettierPlugin,
- },
- rules: {
- 'prettier/prettier': 'error',
- },
- },
-]
diff --git a/.eslintrc b/.eslintrc
deleted file mode 100644
index 9e17846..0000000
--- a/.eslintrc
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "root": true,
- "extends": ["@nuxt/eslint-config"]
-}
diff --git a/.eslintrc.prettier.js b/.eslintrc.prettier.js
deleted file mode 100644
index 7a440c2..0000000
--- a/.eslintrc.prettier.js
+++ /dev/null
@@ -1,24 +0,0 @@
-module.exports = {
- root: true,
- extends: ['@nuxt/eslint-config', 'plugin:prettier/recommended'],
- rules: {
- 'prettier/prettier': 'error',
- '@stylistic/quotes': ['error', 'single'],
- '@stylistic/semi': ['error', 'never'],
- '@stylistic/comma-dangle': ['error', 'always-multiline'],
- '@stylistic/operator-linebreak': ['error', 'before'],
- '@stylistic/member-delimiter-style': [
- 'error',
- {
- multiline: {
- delimiter: 'none',
- requireLast: false,
- },
- singleline: {
- delimiter: 'comma',
- requireLast: false,
- },
- },
- ],
- },
-}
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index c13139c..9a1674d 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -48,6 +48,9 @@ jobs:
- run: pnpm lint --max-warnings 0
+ - name: Check formatting
+ run: pnpm format:check
+
- name: Prepare module types
run: pnpm dev:prepare
diff --git a/.prettierignore b/.prettierignore
index e688f5c..8eb09df 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -19,4 +19,7 @@ coverage
.vscode
# git
-.git
\ No newline at end of file
+.git
+
+# pnpm writes the lockfile in its own format
+pnpm-lock.yaml
diff --git a/README.md b/README.md
index ab62939..e5fb38c 100644
--- a/README.md
+++ b/README.md
@@ -251,6 +251,7 @@ pnpm install --frozen-lockfile
pnpm dev:prepare
pnpm playwright-core install chromium
pnpm lint
+pnpm format:check
pnpm test:types
pnpm test
pnpm test:package
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md
index acc1b73..e638971 100644
--- a/docs/CONTRIBUTING.md
+++ b/docs/CONTRIBUTING.md
@@ -16,4 +16,4 @@ Contact any of our current maintainers, or send us an email at [community@stackb
Use the pnpm version pinned in `package.json`. With Corepack installed, run `corepack enable`, then `pnpm install --frozen-lockfile` and `pnpm dev:prepare` from the repository root. The module and playground share `pnpm-lock.yaml`; install dependencies from the root and use `pnpm --dir playground build` to build the playground directly.
-Before submitting a change, run `pnpm lint`, `pnpm test:types`, `pnpm test`, and `pnpm test:package`. For runtime changes, also run `pnpm test:compatibility` and `NUXT_VERSION=3 pnpm test:compatibility` to test independent static-site consumers.
+Before submitting a change, run `pnpm lint`, `pnpm format:check`, `pnpm test:types`, `pnpm test`, and `pnpm test:package`. Prettier owns formatting and ESLint checks everything else; `pnpm format` applies the formatting. For runtime changes, also run `pnpm test:compatibility` and `NUXT_VERSION=3 pnpm test:compatibility` to test independent static-site consumers.
diff --git a/eslint.config.mjs b/eslint.config.mjs
index 4f44f47..dedcb05 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -1,38 +1,20 @@
// @ts-check
import { createConfigForNuxt } from '@nuxt/eslint-config/flat'
+import prettier from 'eslint-config-prettier/flat'
// Run `npx @eslint/config-inspector` to inspect the resolved config interactively
export default createConfigForNuxt({
features: {
// Rules for module authors
tooling: true,
- // Rules for formatting
- stylistic: true,
+ // Prettier formats the code, so the key order rule that came with stylistic is enabled on its own
+ nuxt: { sortConfigKeys: true },
},
dirs: {
src: ['./playground'],
},
-}).append({
- rules: {
- '@stylistic/quotes': ['error', 'single', { avoidEscape: true }],
- '@stylistic/semi': ['error', 'never'],
- '@stylistic/comma-dangle': ['error', 'always-multiline'],
- '@stylistic/arrow-parens': ['error', 'always'],
- '@stylistic/operator-linebreak': 'off',
- '@stylistic/brace-style': 'off',
- '@stylistic/indent-binary-ops': 'off',
- '@stylistic/member-delimiter-style': [
- 'error',
- {
- multiline: {
- delimiter: 'none',
- requireLast: false,
- },
- singleline: {
- delimiter: 'comma',
- requireLast: false,
- },
- },
- ],
- },
+}).append(prettier, {
+ // eslint-config-prettier turns this off, but without semicolons it is the rule that catches
+ // a new line silently continuing the previous statement
+ rules: { 'no-unexpected-multiline': 'error' },
})
diff --git a/package.json b/package.json
index e2de09a..0ae730c 100644
--- a/package.json
+++ b/package.json
@@ -47,8 +47,8 @@
"dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
"release:prepare": "pnpm run prepack && changelogen --release --no-tag --no-push --no-github",
"lint": "eslint .",
- "lint:prettier": "eslint --config .eslintprettier.config.js .",
"format": "prettier --write .",
+ "format:check": "prettier --check .",
"lint:fix": "eslint . --fix && prettier --write .",
"test": "vitest run",
"test:watch": "vitest watch",
@@ -71,7 +71,6 @@
"changelogen": "^0.6.1",
"eslint": "^10.7.0",
"eslint-config-prettier": "^10.1.5",
- "eslint-plugin-prettier": "^5.4.0",
"hookable": "^6.1.1",
"nuxt": "^4.2.1",
"playwright": "1.63.0",
diff --git a/playground/app.vue b/playground/app.vue
index 4b1f42a..2afe9cf 100644
--- a/playground/app.vue
+++ b/playground/app.vue
@@ -14,28 +14,15 @@
-
-
-
+
+
+
Custom Hook Testing
-
-
+
+
diff --git a/playground/plugins/page-category.client.ts b/playground/plugins/page-category.client.ts
index 7818f9a..212c6fa 100644
--- a/playground/plugins/page-category.client.ts
+++ b/playground/plugins/page-category.client.ts
@@ -5,7 +5,9 @@ export default defineNuxtPlugin(() => {
const utm = useNuxtUTM()
utm.onBeforePersist((data) => {
- const pageCategory = new URL(data.additionalInfo.landingPageUrl).searchParams.get('page_category')
+ const pageCategory = new URL(data.additionalInfo.landingPageUrl).searchParams.get(
+ 'page_category',
+ )
if (!pageCategory) return
data.customParams = {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 800ccd8..323874f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -55,9 +55,6 @@ importers:
eslint-config-prettier:
specifier: ^10.1.5
version: 10.1.8(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))
- eslint-plugin-prettier:
- specifier: ^5.4.0
- version: 5.5.6(eslint-config-prettier@10.1.8(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(prettier@3.9.9)
hookable:
specifier: ^6.1.1
version: 6.1.2
@@ -876,10 +873,6 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
- '@pkgr/core@0.3.6':
- resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==}
- engines: {node: ^14.18.0 || >=16.0.0}
-
'@polka/url@1.0.0-next.29':
resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
@@ -2273,20 +2266,6 @@ packages:
peerDependencies:
eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0
- eslint-plugin-prettier@5.5.6:
- resolution: {integrity: sha512-ifetmTcxWfz+4qRW3pH/ujdTq2jQIj59AxJMIN26K5avYgU8dxycUETQonWiW+wPrYXA0j3Try0l1CnwVQtDqQ==}
- engines: {node: ^14.18.0 || >=16.0.0}
- peerDependencies:
- '@types/eslint': '>=8.0.0'
- eslint: '>=8.0.0'
- eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0'
- prettier: '>=3.0.0'
- peerDependenciesMeta:
- '@types/eslint':
- optional: true
- eslint-config-prettier:
- optional: true
-
eslint-plugin-regexp@3.2.0:
resolution: {integrity: sha512-4yq47CnLxyfHFKJEo5kvNaiJ0aDtBxSJWk4M2LiamplUXdWxdlzDYqImb/ZVCp+2BqkQjBAmw4Xc07Q8SXVDZg==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
@@ -2413,9 +2392,6 @@ packages:
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
- fast-diff@1.3.0:
- resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
-
fast-fifo@1.3.2:
resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==}
@@ -3704,10 +3680,6 @@ packages:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
- prettier-linter-helpers@1.0.1:
- resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==}
- engines: {node: '>=6.0.0'}
-
prettier@3.9.9:
resolution: {integrity: sha512-Z/CJHIkdujO/OtN7nXUii0Rf3VT5SRuhjBA82Xvu2XhBUgX3nhP67T0LHceBdQLex7OOFGTox+Q5Yg8Jk2Qivg==}
engines: {node: '>=14'}
@@ -4092,10 +4064,6 @@ packages:
engines: {node: '>=16'}
hasBin: true
- synckit@0.11.13:
- resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==}
- engines: {node: ^14.18.0 || >=16.0.0}
-
tagged-tag@1.0.0:
resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==}
engines: {node: '>=20'}
@@ -6087,8 +6055,6 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
- '@pkgr/core@0.3.6': {}
-
'@polka/url@1.0.0-next.29': {}
'@poppinss/colors@4.1.6':
@@ -7412,15 +7378,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)))(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))(prettier@3.9.9):
- dependencies:
- eslint: 10.11.0(jiti@2.7.0)(supports-color@10.2.2)
- prettier: 3.9.9
- prettier-linter-helpers: 1.0.1
- synckit: 0.11.13
- optionalDependencies:
- eslint-config-prettier: 10.1.8(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))
-
eslint-plugin-regexp@3.2.0(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2)):
dependencies:
'@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0(jiti@2.7.0)(supports-color@10.2.2))
@@ -7593,8 +7550,6 @@ snapshots:
fast-deep-equal@3.1.3: {}
- fast-diff@1.3.0: {}
-
fast-fifo@1.3.2: {}
fast-glob@3.3.3:
@@ -9301,10 +9256,6 @@ snapshots:
prelude-ls@1.2.1: {}
- prettier-linter-helpers@1.0.1:
- dependencies:
- fast-diff: 1.3.0
-
prettier@3.9.9: {}
pretty-bytes@7.1.3: {}
@@ -9710,10 +9661,6 @@ snapshots:
picocolors: 1.1.1
sax: 1.6.1
- synckit@0.11.13:
- dependencies:
- '@pkgr/core': 0.3.6
-
tagged-tag@1.0.0: {}
tar-stream@3.2.1:
diff --git a/scripts/test-compatibility.mjs b/scripts/test-compatibility.mjs
index cafb411..1f89ce7 100644
--- a/scripts/test-compatibility.mjs
+++ b/scripts/test-compatibility.mjs
@@ -24,8 +24,8 @@ export async function testCompatibility({ root, directory, tarball }) {
private: true,
type: 'module',
packageManager,
- dependencies: { 'nuxt': version, 'nuxt-utm': `file:${tarball}`, 'vue': '^3.5.0' },
- devDependencies: { 'typescript': '~5.9.3', 'vue-tsc': '^3.3.8' },
+ dependencies: { nuxt: version, 'nuxt-utm': `file:${tarball}`, vue: '^3.5.0' },
+ devDependencies: { typescript: '~5.9.3', 'vue-tsc': '^3.3.8' },
}),
)
writeFileSync(
@@ -39,9 +39,12 @@ export async function testCompatibility({ root, directory, tarball }) {
timeout: 300_000,
env: { ...process.env, NUXT_TELEMETRY_DISABLED: '1' },
})
- const allowBuilds = JSON.parse(execFileSync(pnpmCommand, [...pnpmArguments, 'config', 'get', 'allowBuilds', '--json'], {
- cwd: root, encoding: 'utf8',
- }))
+ const allowBuilds = JSON.parse(
+ execFileSync(pnpmCommand, [...pnpmArguments, 'config', 'get', 'allowBuilds', '--json'], {
+ cwd: root,
+ encoding: 'utf8',
+ }),
+ )
writeFileSync(join(fixture, 'pnpm-workspace.yaml'), JSON.stringify({ allowBuilds }))
run(['install', '--no-frozen-lockfile'])
const installed = JSON.parse(
@@ -108,7 +111,13 @@ export async function testCompatibility({ root, directory, tarball }) {
await page.getByRole('button', { name: 'Send', exact: true }).click()
await page.getByRole('status').filter({ hasText: 'Please try again' }).waitFor()
assert.equal(submissions.length, 1)
- assert.equal(await page.getByTestId('history-count').textContent().then((text) => text.trim()), '3')
+ assert.equal(
+ await page
+ .getByTestId('history-count')
+ .textContent()
+ .then((text) => text.trim()),
+ '3',
+ )
await page.getByRole('button', { name: 'Send', exact: true }).click()
await page.getByRole('status').filter({ hasText: 'Sent' }).waitFor()
assert.equal(submissions.length, 2)
diff --git a/test/integration-runtime-control.test.ts b/test/integration-runtime-control.test.ts
index bdad6b7..3017c15 100644
--- a/test/integration-runtime-control.test.ts
+++ b/test/integration-runtime-control.test.ts
@@ -73,7 +73,9 @@ describe('Runtime tracking control', async () => {
let rawData = await page.evaluate(() => window.localStorage.getItem('nuxt-utm-data'))
let entries = JSON.parse(rawData ?? '[]')
- const hasDisabled = entries.some((e: DataObject) => e.utmParams?.utm_source === 'while_disabled')
+ const hasDisabled = entries.some(
+ (e: DataObject) => e.utmParams?.utm_source === 'while_disabled',
+ )
expect(hasDisabled).toBe(false)
// Enable tracking
@@ -119,7 +121,9 @@ describe('Runtime tracking control', async () => {
expect(rawData).toBeNull()
// Check session ID is also cleared
- const sessionId = await page.evaluate(() => window.sessionStorage.getItem('nuxt-utm-session-id'))
+ const sessionId = await page.evaluate(() =>
+ window.sessionStorage.getItem('nuxt-utm-session-id'),
+ )
expect(sessionId).toBeNull()
await page.close()
diff --git a/test/package-fixture/app/pages/contact.vue b/test/package-fixture/app/pages/contact.vue
index b3215ea..674af1a 100644
--- a/test/package-fixture/app/pages/contact.vue
+++ b/test/package-fixture/app/pages/contact.vue
@@ -40,19 +40,8 @@ async function submitLead() {