diff --git a/e2e/acceptance/crate-navtabs.spec.ts b/e2e/acceptance/crate-navtabs.spec.ts index 16c23c5186d..41a7ff5766d 100644 --- a/e2e/acceptance/crate-navtabs.spec.ts +++ b/e2e/acceptance/crate-navtabs.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@/e2e/helper'; +import { expect, test } from '@/e2e/helper'; import { Locator } from '@playwright/test'; test.describe('Acceptance | crate navigation tabs', { tag: '@acceptance' }, () => { diff --git a/e2e/acceptance/login.spec.ts b/e2e/acceptance/login.spec.ts index 2035a28e577..c782675c6f7 100644 --- a/e2e/acceptance/login.spec.ts +++ b/e2e/acceptance/login.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@/e2e/helper'; +import { expect, test } from '@/e2e/helper'; import { http, HttpResponse } from 'msw'; test.describe('Acceptance | Login', { tag: '@acceptance' }, () => { diff --git a/e2e/acceptance/read-only-mode.spec.ts b/e2e/acceptance/read-only-mode.spec.ts index 02f50d91444..a1fff1c8224 100644 --- a/e2e/acceptance/read-only-mode.spec.ts +++ b/e2e/acceptance/read-only-mode.spec.ts @@ -1,4 +1,4 @@ -import { test, expect, AppFixtures } from '@/e2e/helper'; +import { AppFixtures, expect, test } from '@/e2e/helper'; import { http, HttpResponse } from 'msw'; test.describe('Acceptance | Read-only Mode', { tag: '@acceptance' }, () => { diff --git a/e2e/acceptance/sudo.spec.ts b/e2e/acceptance/sudo.spec.ts index c60b23915c0..740ab4100dd 100644 --- a/e2e/acceptance/sudo.spec.ts +++ b/e2e/acceptance/sudo.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@/e2e/helper'; +import { expect, test } from '@/e2e/helper'; import { format } from 'date-fns/format'; test.describe('Acceptance | sudo', { tag: '@acceptance' }, () => { diff --git a/e2e/acceptance/support.spec.ts b/e2e/acceptance/support.spec.ts index ba12b2df8fa..5d4ac390806 100644 --- a/e2e/acceptance/support.spec.ts +++ b/e2e/acceptance/support.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@/e2e/helper'; +import { expect, test } from '@/e2e/helper'; test.describe('Acceptance | support page', { tag: '@acceptance' }, () => { test.beforeEach(async ({ page, msw }) => { diff --git a/e2e/bugs/11772.spec.ts b/e2e/bugs/11772.spec.ts index 442a1c5956f..b5bbcf4be78 100644 --- a/e2e/bugs/11772.spec.ts +++ b/e2e/bugs/11772.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@/e2e/helper'; +import { expect, test } from '@/e2e/helper'; test.describe('Bug #11772', { tag: '@bugs' }, () => { async function prepare(msw: any) { diff --git a/e2e/fixtures/a11y.ts b/e2e/fixtures/a11y.ts index 6f12f886ea9..5c1a8cd1a20 100644 --- a/e2e/fixtures/a11y.ts +++ b/e2e/fixtures/a11y.ts @@ -1,6 +1,5 @@ -import { Page } from '@playwright/test'; import { AxeBuilder } from '@axe-core/playwright'; -import { expect } from '@playwright/test'; +import { expect, Page } from '@playwright/test'; // Ref: https://playwright.dev/docs/accessibility-testing#scanning-an-entire-page export class A11yPage { diff --git a/e2e/fixtures/ember.ts b/e2e/fixtures/ember.ts index c8bbd8ac263..4861219caae 100644 --- a/e2e/fixtures/ember.ts +++ b/e2e/fixtures/ember.ts @@ -1,5 +1,7 @@ +import type _Ember from 'ember'; + +import { APP_HOOK_CUSTOM_EVENTS, APP_HOOK_KEY, SENTRY_HOOK_KEY } from '@/app/consts'; import { Page } from '@playwright/test'; -import { APP_HOOK_KEY, APP_HOOK_CUSTOM_EVENTS, SENTRY_HOOK_KEY } from '@/app/consts'; const HOOK_MAPPING = { hook: APP_HOOK_KEY, @@ -69,8 +71,6 @@ export class EmberPage { } } -import type _Ember from 'ember'; - type _Ember = typeof _Ember; type HookFn = (owner: _Ember.ApplicationInstance, app: _Ember.Application) => void | Promise; type HookScript = Exclude[0], Function>; diff --git a/e2e/fixtures/fake-timers.ts b/e2e/fixtures/fake-timers.ts index d6d80125a05..4abf875fb59 100644 --- a/e2e/fixtures/fake-timers.ts +++ b/e2e/fixtures/fake-timers.ts @@ -1,5 +1,5 @@ import { Page } from '@playwright/test'; -import { withGlobal, FakeTimerWithContext, FakeTimerInstallOpts } from '@sinonjs/fake-timers'; +import { FakeTimerInstallOpts, FakeTimerWithContext, withGlobal } from '@sinonjs/fake-timers'; export type FakeTimersOptions = Omit & { now?: FakeTimerInstallOpts['now'] | string }; diff --git a/e2e/fixtures/percy.ts b/e2e/fixtures/percy.ts index 365003cdcb9..be4b7126e49 100644 --- a/e2e/fixtures/percy.ts +++ b/e2e/fixtures/percy.ts @@ -1,5 +1,5 @@ -import { Page, TestInfo } from '@playwright/test'; import { default as percySnapshot } from '@percy/playwright'; +import { Page, TestInfo } from '@playwright/test'; export class PercyPage { constructor( diff --git a/e2e/helper.ts b/e2e/helper.ts index 6e346d25ffb..d2c83852d34 100644 --- a/e2e/helper.ts +++ b/e2e/helper.ts @@ -1,14 +1,14 @@ +import axeConfig from '@/tests/axe-config'; +import { db, handlers } from '@crates-io/msw'; import { test as base } from '@playwright/test'; +import * as pwFakeTimers from '@sinonjs/fake-timers'; import type { MockServiceWorker } from 'playwright-msw'; import { createWorker } from 'playwright-msw'; -import { db, handlers } from '@crates-io/msw'; -import * as pwFakeTimers from '@sinonjs/fake-timers'; -import { FakeTimers, FakeTimersOptions } from './fixtures/fake-timers'; -import { PercyPage } from './fixtures/percy'; import { A11yPage } from './fixtures/a11y'; import { EmberPage, EmberPageOptions } from './fixtures/ember'; -import axeConfig from '@/tests/axe-config'; +import { FakeTimers, FakeTimersOptions } from './fixtures/fake-timers'; +import { PercyPage } from './fixtures/percy'; export type AppOptions = { clockOptions: FakeTimersOptions; diff --git a/e2e/routes/crate/settings.spec.ts b/e2e/routes/crate/settings.spec.ts index 82c09b66d9f..be72adeeade 100644 --- a/e2e/routes/crate/settings.spec.ts +++ b/e2e/routes/crate/settings.spec.ts @@ -1,5 +1,5 @@ -import { expect, test } from '@/e2e/helper'; import { defer } from '@/e2e/deferred'; +import { expect, test } from '@/e2e/helper'; import { http, HttpResponse } from 'msw'; test.describe('Route | crate.settings', { tag: '@routes' }, () => { diff --git a/e2e/routes/crate/settings/new-trusted-publisher.spec.ts b/e2e/routes/crate/settings/new-trusted-publisher.spec.ts index ee54e1d91f6..86425dfc151 100644 --- a/e2e/routes/crate/settings/new-trusted-publisher.spec.ts +++ b/e2e/routes/crate/settings/new-trusted-publisher.spec.ts @@ -1,6 +1,6 @@ +import { defer } from '@/e2e/deferred'; import { expect, test } from '@/e2e/helper'; import { http, HttpResponse } from 'msw'; -import { defer } from '@/e2e/deferred'; test.describe('Route | crate.settings.new-trusted-publisher', { tag: '@routes' }, () => { async function prepare(msw) { diff --git a/e2e/routes/support.spec.ts b/e2e/routes/support.spec.ts index a4cdf0fcd62..7a7a194e03d 100644 --- a/e2e/routes/support.spec.ts +++ b/e2e/routes/support.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@/e2e/helper'; +import { expect, test } from '@/e2e/helper'; test.describe('Route | support', { tag: '@routes' }, () => { test('should not retain query params when exiting and then returning', async ({ page }) => { diff --git a/eslint.config.mjs b/eslint.config.mjs index 72af1b3077e..121e2a6eff8 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,14 +1,14 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + import { FlatCompat } from '@eslint/eslintrc'; import js from '@eslint/js'; import emberEslintParser from 'ember-eslint-parser'; import ember from 'eslint-plugin-ember'; import emberConcurrency from 'eslint-plugin-ember-concurrency'; -import importHelpers from 'eslint-plugin-import-helpers'; import prettier from 'eslint-plugin-prettier'; import eslintPluginUnicorn from 'eslint-plugin-unicorn'; import globals from 'globals'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; const filename = fileURLToPath(import.meta.url); const dirname = path.dirname(filename); @@ -59,7 +59,6 @@ export default [ ember, 'ember-concurrency': emberConcurrency, prettier, - 'import-helpers': importHelpers, }, languageOptions: { @@ -91,9 +90,6 @@ export default [ // it's fine to use `return` without a value and rely on the implicit `undefined` return value 'getter-return': 'off', - // declaration sort is taken care of by `import-helpers/order-imports` - 'sort-imports': ['error', { ignoreDeclarationSort: true, ignoreCase: true }], - 'prettier/prettier': 'error', // disabled because we still use `this.set()` in a few places and it works just fine @@ -145,25 +141,6 @@ export default [ // disabled because of false positives 'unicorn/consistent-destructuring': 'off', 'unicorn/filename-case': ['error', { case: 'kebabCase', ignore: ['^-'] }], - - 'import-helpers/order-imports': [ - 'error', - { - newlinesBetween: 'always', - groups: [ - // Node.js built-in modules - '/^(assert|async_hooks|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|http2|https|inspector|module|net|os|path|perf_hooks|process|punycode|querystring|readline|repl|stream|string_decoder|timers|tls|trace_events|tty|url|util|v8|vm|zli)/', - // Testing modules - ['/^(qunit|ember-qunit|@ember/test-helpers|htmlbars-inline-precompile)$/', '/^ember-exam\\//'], - // Ember.js modules - ['/^@(ember|ember-data|glimmer)\\//', '/^(ember|ember-data|rsvp)$/', '/^ember-data\\//'], - ['module'], - ['/^crates-io\\//'], - ['parent', 'sibling', 'index'], - ], - alphabetize: { order: 'asc', ignoreCase: true }, - }, - ], }, }, diff --git a/package.json b/package.json index 0a6d92d16eb..2124b89dc63 100644 --- a/package.json +++ b/package.json @@ -34,9 +34,30 @@ }, "prettier": { "plugins": [ + "@ianvs/prettier-plugin-sort-imports", "prettier-plugin-ember-template-tag" ], "arrowParens": "avoid", + "importOrder": [ + "^(node:)?(assert|async_hooks|buffer|child_process|cluster|console|constants|crypto|dgram|dns|domain|events|fs|http|http2|https|inspector|module|net|os|path|perf_hooks|process|punycode|querystring|readline|repl|stream|string_decoder|timers|tls|trace_events|tty|url|util|v8|vm|zli)", + "", + "^(qunit|ember-qunit|@ember/test-helpers|htmlbars-inline-precompile)$", + "^ember-exam/", + "", + "^@(ember|ember-data|glimmer)/", + "^(ember|ember-data|rsvp)$", + "^ember-data/", + "", + "", + "", + "^crates-io/", + "", + "^[.]" + ], + "importOrderParserPlugins": [ + "decorators", + "typescript" + ], "printWidth": 120, "singleQuote": true, "tabWidth": 2, @@ -72,6 +93,7 @@ "@eslint/js": "9.39.1", "@glimmer/component": "2.0.0", "@glimmer/tracking": "1.1.2", + "@ianvs/prettier-plugin-sort-imports": "^4.7.0", "@mainmatter/ember-api-actions": "0.6.0", "@percy/cli": "1.31.5", "@percy/ember": "5.0.0", @@ -123,7 +145,6 @@ "eslint-config-prettier": "10.1.8", "eslint-plugin-ember": "12.7.5", "eslint-plugin-ember-concurrency": "0.5.1", - "eslint-plugin-import-helpers": "2.0.1", "eslint-plugin-prettier": "5.5.4", "eslint-plugin-qunit": "8.2.5", "eslint-plugin-qunit-dom": "0.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 680edd3ad17..1057ec44d67 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -99,6 +99,9 @@ importers: '@glimmer/tracking': specifier: 1.1.2 version: 1.1.2 + '@ianvs/prettier-plugin-sort-imports': + specifier: ^4.7.0 + version: 4.7.0(content-tag@4.0.0)(prettier-plugin-ember-template-tag@2.1.2(prettier@3.7.4))(prettier@3.7.4) '@mainmatter/ember-api-actions': specifier: 0.6.0 version: 0.6.0 @@ -252,9 +255,6 @@ importers: eslint-plugin-ember-concurrency: specifier: 0.5.1 version: 0.5.1(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-import-helpers: - specifier: 2.0.1 - version: 2.0.1(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-prettier: specifier: 5.5.4 version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.7.4) @@ -1774,6 +1774,24 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@ianvs/prettier-plugin-sort-imports@4.7.0': + resolution: {integrity: sha512-soa2bPUJAFruLL4z/CnMfSEKGznm5ebz29fIa9PxYtu8HHyLKNE1NXAs6dylfw1jn/ilEIfO2oLLN6uAafb7DA==} + peerDependencies: + '@prettier/plugin-oxc': ^0.0.4 + '@vue/compiler-sfc': 2.7.x || 3.x + content-tag: ^4.0.0 + prettier: 2 || 3 || ^4.0.0-0 + prettier-plugin-ember-template-tag: ^2.1.0 + peerDependenciesMeta: + '@prettier/plugin-oxc': + optional: true + '@vue/compiler-sfc': + optional: true + content-tag: + optional: true + prettier-plugin-ember-template-tag: + optional: true + '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -2022,8 +2040,8 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@rollup/wasm-node@4.53.3': - resolution: {integrity: sha512-mB8z32H6kz4kVjn+tfTGcrXBae7rIeAvm/g6itsE3IqcXpjSRRvk1/EOWDEi5wL8NNmxXiH71t4jtNfr128zpw==} + '@rollup/wasm-node@4.53.4': + resolution: {integrity: sha512-DrRa/kpp8buB0tzzmuBom4nCfu1mvGentblZihLsI3BViEUGLkybfZ4w8ro6kDOtdtJGKMIzbsHkeGXpggIaag==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -4610,11 +4628,6 @@ packages: '@typescript-eslint/parser': optional: true - eslint-plugin-import-helpers@2.0.1: - resolution: {integrity: sha512-MSiQZV0k+bQlt7RnHZrJxDBE948urZthusQt78FKmQ5/WLm5PpS8L16+2JVGBiSffneOAe4fud8RdYmqPveo8A==} - peerDependencies: - eslint: 9.x - eslint-plugin-prettier@5.5.4: resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==} engines: {node: ^14.18.0 || >=16.0.0} @@ -10446,6 +10459,20 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@ianvs/prettier-plugin-sort-imports@4.7.0(content-tag@4.0.0)(prettier-plugin-ember-template-tag@2.1.2(prettier@3.7.4))(prettier@3.7.4)': + dependencies: + '@babel/generator': 7.28.5 + '@babel/parser': 7.28.5 + '@babel/traverse': 7.28.5(supports-color@8.1.1) + '@babel/types': 7.28.5 + prettier: 3.7.4 + semver: 7.7.3 + optionalDependencies: + content-tag: 4.0.0 + prettier-plugin-ember-template-tag: 2.1.2(prettier@3.7.4) + transitivePeerDependencies: + - supports-color + '@iconify/types@2.0.0': {} '@iconify/utils@3.1.0': @@ -10806,7 +10833,7 @@ snapshots: '@polka/url@1.0.0-next.29': {} - '@rollup/wasm-node@4.53.3': + '@rollup/wasm-node@4.53.4': dependencies: '@types/estree': 1.0.8 optionalDependencies: @@ -14332,10 +14359,6 @@ snapshots: - '@babel/core' - typescript - eslint-plugin-import-helpers@2.0.1(eslint@9.39.1(jiti@2.6.1)): - dependencies: - eslint: 9.39.1(jiti@2.6.1) - eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.7.4): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -18548,7 +18571,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: '@rollup/wasm-node@4.53.3' + rollup: '@rollup/wasm-node@4.53.4' tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.10.3 diff --git a/script/generate-deploy-changelog.mjs b/script/generate-deploy-changelog.mjs index ce78bb92886..c670dedbf09 100755 --- a/script/generate-deploy-changelog.mjs +++ b/script/generate-deploy-changelog.mjs @@ -1,5 +1,4 @@ #!/usr/bin/env node - import { execSync } from 'node:child_process'; function exec(command, options = {}) { diff --git a/script/precompress-assets.mjs b/script/precompress-assets.mjs index c092b0c8e30..e63c0d65fdd 100644 --- a/script/precompress-assets.mjs +++ b/script/precompress-assets.mjs @@ -1,9 +1,10 @@ -import { globby } from 'globby'; import { createReadStream, createWriteStream } from 'node:fs'; import { stat } from 'node:fs/promises'; import { pipeline } from 'node:stream/promises'; import { constants, createBrotliCompress, createGzip } from 'node:zlib'; +import { globby } from 'globby'; + let paths = await globby(['**/*.css', '**/*.html', '**/*.js', '**/*.map', '**/*.svg', '**/*.txt', '**/*.xml'], { cwd: 'dist', });