├── tests ├── unit │ ├── .gitkeep │ ├── deprecation-collector-test.js │ ├── flush-deprecations-test.js │ └── handle-deprecation-workflow-test.js ├── helpers │ └── debug-test.js ├── index.html ├── test-helper.js ├── deprecation-workflow.js └── acceptance │ ├── flush-deprecations-test.js │ └── workflow-config-test.js ├── addon-main.cjs ├── demo-app ├── templates │ └── application.gts ├── styles.css └── app.gts ├── .prettierignore ├── .gitignore ├── .prettierrc.mjs ├── .env.development ├── pnpm-workspace.yaml ├── .editorconfig ├── src ├── index.d.ts └── index.js ├── config └── ember-cli-update.json ├── index.html ├── vite.config.mjs ├── CONTRIBUTING.md ├── testem.cjs ├── LICENSE.md ├── babel.config.cjs ├── .github └── workflows │ ├── publish.yml │ ├── plan-release.yml │ └── ci.yml ├── .release-plan.json ├── RELEASE.md ├── .try.mjs ├── eslint.config.mjs ├── package.json ├── README.md └── CHANGELOG.md /tests/unit/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /addon-main.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { addonV1Shim } = require('@embroider/addon-shim'); 4 | module.exports = addonV1Shim(__dirname); 5 | -------------------------------------------------------------------------------- /demo-app/templates/application.gts: -------------------------------------------------------------------------------- 1 | import { pageTitle } from 'ember-page-title'; 2 | 3 | const greeting = 'hello'; 4 | 5 | 12 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | 4 | # compiled output 5 | /dist/ 6 | /dist-*/ 7 | /declarations/ 8 | 9 | # misc 10 | /coverage/ 11 | pnpm-lock.yaml 12 | config/ember-cli-update.json 13 | *.yaml 14 | *.yml 15 | *.md 16 | *.html 17 | -------------------------------------------------------------------------------- /demo-app/styles.css: -------------------------------------------------------------------------------- 1 | /** 2 | * See: https://vite.dev/guide/features.html#css 3 | * for features beyond normal CSS that are available to you. 4 | * 5 | * This CSS is meant for the demo-app only, and will not be included in the published assets for this library. 6 | */ 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | dist/ 3 | dist-tests/ 4 | declarations/ 5 | 6 | # from scenarios 7 | tmp/ 8 | config/optional-features.json 9 | ember-cli-build.cjs 10 | 11 | # npm/pnpm/yarn pack output 12 | *.tgz 13 | 14 | # deps & caches 15 | node_modules/ 16 | .eslintcache 17 | .prettiercache 18 | -------------------------------------------------------------------------------- /.prettierrc.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: ['prettier-plugin-ember-template-tag'], 3 | overrides: [ 4 | { 5 | files: '*.{js,gjs,ts,gts,mjs,mts,cjs,cts}', 6 | options: { 7 | singleQuote: true, 8 | templateSingleQuote: false, 9 | }, 10 | }, 11 | ], 12 | }; 13 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # This file is committed to git and should not contain any secrets. 2 | # 3 | # Vite recommends using .env.local or .env.[mode].local if you need to manage secrets 4 | # SEE: https://vite.dev/guide/env-and-mode.html#env-files for more information. 5 | 6 | 7 | # Default NODE_ENV with vite build --mode=test is production 8 | NODE_ENV=development 9 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "." 3 | 4 | # Docs: https://pnpm.io/settings 5 | # https://github.com/emberjs/rfcs/pull/907 6 | 7 | # we don't want addons to be bad citizens of the ecosystem 8 | autoInstallPeers: false 9 | 10 | # we want true isolation, 11 | # if a dependency is not declared, we want an error 12 | resolvePeersFromWorkspaceRoot: false 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.hbs] 16 | insert_final_newline = false 17 | 18 | [*.{diff,md}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /tests/helpers/debug-test.js: -------------------------------------------------------------------------------- 1 | import { DEBUG } from '@glimmer/env'; 2 | import { test } from 'qunit'; 3 | 4 | export default function debugTest(description, callback) { 5 | return test(description, function (assert) { 6 | if (!DEBUG) { 7 | assert.pushResult({ 8 | result: true, 9 | message: 'debug functions are disabled', 10 | }); 11 | return; 12 | } 13 | 14 | return callback.apply(this, arguments); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | interface WorkflowMatchId { 2 | handler?: 'log' | 'silence' | 'throw'; 3 | matchId: string | RegExp; 4 | } 5 | 6 | interface WorkflowMatchMessage { 7 | handler?: 'log' | 'silence' | 'throw'; 8 | matchMessage: string | RegExp; 9 | } 10 | 11 | type Workflow = WorkflowMatchId | WorkflowMatchMessage; 12 | 13 | export default function setupDeprecationWorkflow(config?: { 14 | throwOnUnhandled?: boolean; 15 | workflow?: Workflow[]; 16 | }): void; 17 | -------------------------------------------------------------------------------- /config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "projectName": "ember-cli-deprecation-workflow", 4 | "packages": [ 5 | { 6 | "name": "@ember/addon-blueprint", 7 | "version": "0.16.1", 8 | "blueprints": [ 9 | { 10 | "name": "@ember/addon-blueprint", 11 | "isBaseBlueprint": true, 12 | "options": [ 13 | "--ci-provider=github", 14 | "--pnpm" 15 | ] 16 | } 17 | ] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Demo App 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /vite.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import { extensions, ember, classicEmberSupport } from '@embroider/vite'; 3 | import { babel } from '@rollup/plugin-babel'; 4 | 5 | // For scenario testing 6 | const isCompat = Boolean(process.env.ENABLE_COMPAT_BUILD); 7 | 8 | export default defineConfig({ 9 | plugins: [ 10 | ...(isCompat ? [classicEmberSupport()] : []), 11 | ember(), 12 | babel({ 13 | babelHelpers: 'inline', 14 | extensions, 15 | }), 16 | ], 17 | build: { 18 | rollupOptions: { 19 | input: { 20 | tests: 'tests/index.html', 21 | }, 22 | }, 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How To Contribute 2 | 3 | ## Installation 4 | 5 | - `git clone ` 6 | - `cd ember-cli-deprecation-workflow` 7 | - `pnpm install` 8 | 9 | ## Linting 10 | 11 | - `pnpm lint` 12 | - `pnpm lint:fix` 13 | 14 | ## Building the addon 15 | 16 | - `pnpm build` 17 | 18 | ## Running tests 19 | 20 | - `pnpm test` – Runs the test suite on the current Ember version 21 | - `pnpm test:watch` – Runs the test suite in "watch mode" 22 | 23 | ## Running the test application 24 | 25 | - `pnpm start` 26 | - Visit the test application at [http://localhost:4200](http://localhost:4200). 27 | 28 | For more information on using ember-cli, visit [https://cli.emberjs.com/release/](https://cli.emberjs.com/release/). 29 | -------------------------------------------------------------------------------- /testem.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof module !== 'undefined') { 4 | module.exports = { 5 | test_page: 'tests/index.html?hidepassed', 6 | cwd: 'dist-tests', 7 | disable_watching: true, 8 | launch_in_ci: ['Chrome'], 9 | launch_in_dev: ['Chrome'], 10 | browser_start_timeout: 120, 11 | browser_args: { 12 | Chrome: { 13 | ci: [ 14 | // --no-sandbox is needed when running Chrome inside a container 15 | process.env.CI ? '--no-sandbox' : null, 16 | '--headless=new', 17 | '--disable-dev-shm-usage', 18 | '--disable-software-rasterizer', 19 | '--mute-audio', 20 | '--remote-debugging-port=0', 21 | '--window-size=1440,900', 22 | ].filter(Boolean), 23 | }, 24 | }, 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ember-cli-deprecation-workflow Tests 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 |
16 | 17 | 18 | 21 | 22 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import EmberApp from 'ember-strict-application-resolver'; 2 | import EmberRouter from '@ember/routing/router'; 3 | import * as QUnit from 'qunit'; 4 | import { setApplication } from '@ember/test-helpers'; 5 | import { setup } from 'qunit-dom'; 6 | import { start as qunitStart, setupEmberOnerrorValidation } from 'ember-qunit'; 7 | import './deprecation-workflow'; 8 | 9 | class Router extends EmberRouter { 10 | location = 'none'; 11 | rootURL = '/'; 12 | } 13 | 14 | class TestApp extends EmberApp { 15 | modules = { 16 | './router': Router, 17 | // add any custom services here 18 | // import.meta.glob('./services/*', { eager: true }), 19 | }; 20 | } 21 | 22 | Router.map(function () {}); 23 | 24 | export function start() { 25 | setApplication( 26 | TestApp.create({ 27 | autoboot: false, 28 | rootElement: '#ember-testing', 29 | }), 30 | ); 31 | setup(QUnit.assert); 32 | setupEmberOnerrorValidation(); 33 | qunitStart(); 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /babel.config.cjs: -------------------------------------------------------------------------------- 1 | /** 2 | * This babel.config is not used for publishing. 3 | * It's only for the local editing experience 4 | * (and linting) 5 | */ 6 | const { buildMacros } = require('@embroider/macros/babel'); 7 | 8 | const { 9 | babelCompatSupport, 10 | templateCompatSupport, 11 | } = require('@embroider/compat/babel'); 12 | 13 | const macros = buildMacros(); 14 | 15 | // For scenario testing 16 | const isCompat = Boolean(process.env.ENABLE_COMPAT_BUILD); 17 | 18 | module.exports = { 19 | plugins: [ 20 | [ 21 | 'babel-plugin-ember-template-compilation', 22 | { 23 | transforms: [ 24 | ...(isCompat ? templateCompatSupport() : macros.templateMacros), 25 | ], 26 | }, 27 | ], 28 | [ 29 | 'module:decorator-transforms', 30 | { 31 | runtime: { 32 | import: require.resolve('decorator-transforms/runtime-esm'), 33 | }, 34 | }, 35 | ], 36 | ...(isCompat ? babelCompatSupport() : macros.babelMacros), 37 | ], 38 | 39 | generatorOpts: { 40 | compact: false, 41 | }, 42 | }; 43 | -------------------------------------------------------------------------------- /tests/deprecation-workflow.js: -------------------------------------------------------------------------------- 1 | import setupDeprecationWorkflow from '#src/index.js'; 2 | 3 | // We export this here to be able to import from our own tests 4 | export const config = { 5 | throwOnUnhandled: true, 6 | workflow: [ 7 | /* 8 | * Actual controlled deprecations 9 | * 10 | * The ember-global log configuration is only required for 11 | * ember-3.28-with-jquery. All other ember-try scenarios pass with that 12 | * handler removed (and defaulting to a throw). 13 | */ 14 | { matchId: 'ember-global', handler: 'log' }, 15 | { matchMessage: /ember-test-waiters/, handler: 'log' }, 16 | 17 | /* 18 | * Deprecation setup for tests 19 | */ 20 | { matchId: 'silence-strict', handler: 'silence' }, 21 | { matchMessage: 'silence-strict', handler: 'silence' }, 22 | { matchMessage: /silence-match/, handler: 'silence' }, 23 | 24 | { matchId: 'log-strict', handler: 'log' }, 25 | { matchMessage: 'log-strict', handler: 'log' }, 26 | { matchMessage: /log-match/, handler: 'log' }, 27 | 28 | { matchMessage: 'throw-strict', handler: 'throw' }, 29 | ], 30 | }; 31 | 32 | setupDeprecationWorkflow(config); 33 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | # For every push to the primary branch with .release-plan.json modified, 2 | # runs release-plan. 3 | 4 | name: Publish Stable 5 | 6 | on: 7 | workflow_dispatch: 8 | push: 9 | branches: 10 | - main 11 | - master 12 | paths: 13 | - ".release-plan.json" 14 | 15 | concurrency: 16 | group: publish-${{ github.head_ref || github.ref }} 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | publish: 21 | name: "NPM Publish" 22 | runs-on: ubuntu-latest 23 | permissions: 24 | contents: write 25 | id-token: write 26 | attestations: write 27 | 28 | steps: 29 | - uses: actions/checkout@v6 30 | - uses: pnpm/action-setup@v4 31 | - uses: actions/setup-node@v6 32 | with: 33 | node-version: 22 34 | registry-url: "https://registry.npmjs.org" 35 | cache: pnpm 36 | - run: pnpm install --frozen-lockfile 37 | - run: npm install -g npm@latest # ensure that the globally installed npm is new enough to support OIDC 38 | - name: Publish to NPM 39 | run: NPM_CONFIG_PROVENANCE=true pnpm release-plan publish 40 | env: 41 | GITHUB_AUTH: ${{ secrets.GITHUB_TOKEN }} 42 | -------------------------------------------------------------------------------- /demo-app/app.gts: -------------------------------------------------------------------------------- 1 | import EmberApp from 'ember-strict-application-resolver'; 2 | import EmberRouter from '@ember/routing/router'; 3 | import PageTitleService from 'ember-page-title/services/page-title'; 4 | 5 | class Router extends EmberRouter { 6 | location = 'history'; 7 | rootURL = '/'; 8 | } 9 | 10 | export class App extends EmberApp { 11 | /** 12 | * Any services or anything from the addon that needs to be in the app-tree registry 13 | * will need to be manually specified here. 14 | * 15 | * Techniques to avoid needing this: 16 | * - private services 17 | * - require the consuming app import and configure themselves 18 | * (which is what we're emulating here) 19 | */ 20 | modules = { 21 | './router': Router, 22 | './services/page-title': PageTitleService, 23 | /** 24 | * NOTE: this glob will import everything matching the glob, 25 | * and includes non-services in the services directory. 26 | */ 27 | ...import.meta.glob('./services/**/*', { eager: true }), 28 | /** 29 | * These imports are not magic, but we do require that all entries in the 30 | * modules object match a ./[type]/[name] pattern. 31 | * 32 | * See: https://rfcs.emberjs.com/id/1132-default-strict-resolver 33 | */ 34 | ...import.meta.glob('./templates/**/*', { eager: true }), 35 | }; 36 | } 37 | 38 | Router.map(function () {}); 39 | -------------------------------------------------------------------------------- /.release-plan.json: -------------------------------------------------------------------------------- 1 | { 2 | "solution": { 3 | "ember-cli-deprecation-workflow": { 4 | "impact": "major", 5 | "oldVersion": "3.4.0", 6 | "newVersion": "4.0.0", 7 | "tagName": "latest", 8 | "constraints": [ 9 | { 10 | "impact": "major", 11 | "reason": "Appears in changelog section :boom: Breaking Change" 12 | }, 13 | { 14 | "impact": "patch", 15 | "reason": "Appears in changelog section :house: Internal" 16 | } 17 | ], 18 | "pkgJSONPath": "./package.json" 19 | } 20 | }, 21 | "description": "## Release (2025-12-18)\n \n* ember-cli-deprecation-workflow 4.0.0 (major)\n\n#### :boom: Breaking Change\n* `ember-cli-deprecation-workflow`\n * [#222](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/222) Convert to v2 Addon - classic apps (using ember-cli for build) will want to remain on v3 ([@NullVoxPopuli](https://github.com/NullVoxPopuli))\n\n#### :house: Internal\n* `ember-cli-deprecation-workflow`\n * [#223](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/223) Update release plan ([@NullVoxPopuli](https://github.com/NullVoxPopuli))\n * [#220](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/220) chore: Test latest LTS versions ([@TSenter](https://github.com/TSenter))\n\n#### Committers: 2\n- Tyler Senter ([@TSenter](https://github.com/TSenter))\n- [@NullVoxPopuli](https://github.com/NullVoxPopuli)\n" 22 | } 23 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release Process 2 | 3 | Releases in this repo are mostly automated using [release-plan](https://github.com/embroider-build/release-plan/). Once you label all your PRs correctly (see below) you will have an automatically generated PR that updates your CHANGELOG.md file and a `.release-plan.json` that is used to prepare the release once the PR is merged. 4 | 5 | ## Preparation 6 | 7 | Since the majority of the actual release process is automated, the remaining tasks before releasing are: 8 | 9 | - correctly labeling **all** pull requests that have been merged since the last release 10 | - updating pull request titles so they make sense to our users 11 | 12 | Some great information on why this is important can be found at [keepachangelog.com](https://keepachangelog.com/en/1.1.0/), but the overall 13 | guiding principle here is that changelogs are for humans, not machines. 14 | 15 | When reviewing merged PR's the labels to be used are: 16 | 17 | - breaking - Used when the PR is considered a breaking change. 18 | - enhancement - Used when the PR adds a new feature or enhancement. 19 | - bug - Used when the PR fixes a bug included in a previous release. 20 | - documentation - Used when the PR adds or updates documentation. 21 | - internal - Internal changes or things that don't fit in any other category. 22 | 23 | **Note:** `release-plan` requires that **all** PRs are labeled. If a PR doesn't fit in a category it's fine to label it as `internal` 24 | 25 | ## Release 26 | 27 | Once the prep work is completed, the actual release is straight forward: you just need to merge the open [Plan Release](https://github.com/ember-cli/ember-cli-deprecation-workflow/pulls?q=is%3Apr+is%3Aopen+%22Prepare+Release%22+in%3Atitle) PR 28 | -------------------------------------------------------------------------------- /tests/acceptance/flush-deprecations-test.js: -------------------------------------------------------------------------------- 1 | import { deprecate } from '@ember/debug'; 2 | import { module } from 'qunit'; 3 | import test from '../helpers/debug-test'; 4 | import { config } from '../deprecation-workflow'; 5 | 6 | let originalWarn; 7 | 8 | module('flushDeprecations', function (hooks) { 9 | hooks.beforeEach(function () { 10 | originalWarn = window.Testem.handleConsoleMessage; 11 | }); 12 | 13 | hooks.afterEach(function () { 14 | window.deprecationWorkflow.deprecationLog = { messages: new Set() }; 15 | window.Testem.handleConsoleMessage = originalWarn; 16 | }); 17 | 18 | test('works', function (assert) { 19 | deprecate('silence-strict', false, { 20 | since: '2.0.0', 21 | until: 'forever', 22 | id: 'test', 23 | for: 'testing', 24 | }); 25 | 26 | deprecate('log-strict', false, { 27 | since: '2.0.0', 28 | until: 'forever', 29 | id: 'test', 30 | for: 'testing', 31 | }); 32 | 33 | deprecate(' foo log-match foo', false, { 34 | since: 'now', 35 | until: 'forever', 36 | id: 'test', 37 | for: 'testing', 38 | }); 39 | 40 | deprecate(' foo foo', false, { 41 | since: 'now', 42 | until: 'forever', 43 | id: 'log-strict', 44 | for: 'testing', 45 | }); 46 | 47 | deprecate('arbitrary-unmatched-message', false, { 48 | id: 'log-strict', 49 | since: '2.0.0', 50 | until: '3.0.0', 51 | for: 'testing', 52 | }); 53 | 54 | const deprecationsPayload = self.deprecationWorkflow.flushDeprecations(); 55 | 56 | assert.strictEqual( 57 | deprecationsPayload, 58 | `import setupDeprecationWorkflow from 'ember-cli-deprecation-workflow'; 59 | 60 | setupDeprecationWorkflow(${JSON.stringify( 61 | { 62 | ...config, 63 | workflow: [ 64 | ...config.workflow, 65 | { handler: 'silence', matchId: 'test' }, 66 | // this one is already present in the existing config, so don't expect another entry, as we deduplicate 67 | // { handler: 'silence', matchId: 'log-strict' }, 68 | ], 69 | }, 70 | undefined, 71 | 2, 72 | )});`, 73 | ); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /.github/workflows/plan-release.yml: -------------------------------------------------------------------------------- 1 | name: Plan Release 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | pull_request_target: # This workflow has permissions on the repo, do NOT run code from PRs in this workflow. See https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ 9 | types: 10 | - labeled 11 | - unlabeled 12 | 13 | concurrency: 14 | group: plan-release # only the latest one of these should ever be running 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | should-run-release-plan-prepare: 19 | name: Should we run release-plan prepare? 20 | runs-on: ubuntu-latest 21 | outputs: 22 | should-prepare: ${{ steps.should-prepare.outputs.should-prepare }} 23 | steps: 24 | - uses: release-plan/actions/should-prepare-release@v1 25 | with: 26 | ref: 'main' 27 | id: should-prepare 28 | 29 | create-prepare-release-pr: 30 | name: Create Prepare Release PR 31 | runs-on: ubuntu-latest 32 | timeout-minutes: 5 33 | needs: should-run-release-plan-prepare 34 | permissions: 35 | contents: write 36 | issues: read 37 | pull-requests: write 38 | if: needs.should-run-release-plan-prepare.outputs.should-prepare == 'true' 39 | steps: 40 | - uses: release-plan/actions/prepare@v1 41 | name: Run release-plan prepare 42 | with: 43 | ref: 'main' 44 | env: 45 | GITHUB_AUTH: ${{ secrets.GITHUB_TOKEN }} 46 | id: explanation 47 | 48 | - uses: peter-evans/create-pull-request@v7 49 | name: Create Prepare Release PR 50 | with: 51 | commit-message: "Prepare Release ${{ steps.explanation.outputs.new-version}} using 'release-plan'" 52 | labels: "internal" 53 | sign-commits: true 54 | branch: release-preview 55 | title: Prepare Release ${{ steps.explanation.outputs.new-version }} 56 | body: | 57 | This PR is a preview of the release that [release-plan](https://github.com/embroider-build/release-plan) has prepared. To release you should just merge this PR 👍 58 | 59 | ----------------------------------------- 60 | 61 | ${{ steps.explanation.outputs.text }} 62 | -------------------------------------------------------------------------------- /.try.mjs: -------------------------------------------------------------------------------- 1 | // When building your addon for older Ember versions you need to have the required files 2 | const compatFiles = { 3 | 'ember-cli-build.cjs': `const EmberApp = require('ember-cli/lib/broccoli/ember-app'); 4 | const { compatBuild } = require('@embroider/compat'); 5 | module.exports = async function (defaults) { 6 | const { buildOnce } = await import('@embroider/vite'); 7 | let app = new EmberApp(defaults); 8 | return compatBuild(app, buildOnce); 9 | };`, 10 | 'config/optional-features.json': JSON.stringify({ 11 | 'application-template-wrapper': false, 12 | 'default-async-observers': true, 13 | 'jquery-integration': false, 14 | 'template-only-glimmer-components': true, 15 | 'no-implicit-route-model': true, 16 | }), 17 | }; 18 | 19 | const compatDeps = { 20 | '@embroider/compat': '^4.0.3', 21 | 'ember-cli': '^5.12.0', 22 | 'ember-auto-import': '^2.10.0', 23 | '@ember/optional-features': '^2.2.0', 24 | }; 25 | 26 | const ancientDeps = { 27 | 'ember-cli': '^4.12.0', 28 | }; 29 | 30 | function compatLTS(version, options = {}) { 31 | return { 32 | name: `ember-lts-${version}`, 33 | npm: { 34 | devDependencies: { 35 | 'ember-source': `~${version}`, 36 | ...compatDeps, 37 | ...options?.deps, 38 | }, 39 | }, 40 | env: { 41 | ENABLE_COMPAT_BUILD: true, 42 | }, 43 | files: compatFiles, 44 | }; 45 | } 46 | 47 | export default { 48 | scenarios: [ 49 | compatLTS('3.28', { deps: ancientDeps }), 50 | compatLTS('4.4'), 51 | compatLTS('4.8'), 52 | compatLTS('4.12'), 53 | compatLTS('5.4'), 54 | compatLTS('5.8'), 55 | compatLTS('5.12'), 56 | { 57 | name: 'ember-lts-6.4', 58 | npm: { 59 | devDependencies: { 60 | 'ember-source': 'npm:ember-source@~6.4.0', 61 | }, 62 | }, 63 | }, 64 | { 65 | name: 'ember-latest', 66 | npm: { 67 | devDependencies: { 68 | 'ember-source': 'npm:ember-source@latest', 69 | }, 70 | }, 71 | }, 72 | { 73 | name: 'ember-beta', 74 | npm: { 75 | devDependencies: { 76 | 'ember-source': 'npm:ember-source@beta', 77 | }, 78 | }, 79 | }, 80 | { 81 | name: 'ember-alpha', 82 | npm: { 83 | devDependencies: { 84 | 'ember-source': 'npm:ember-source@alpha', 85 | }, 86 | }, 87 | }, 88 | ], 89 | }; 90 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | pull_request: {} 9 | 10 | concurrency: 11 | group: ci-${{ github.head_ref || github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | lint: 16 | name: "Lints" 17 | runs-on: ubuntu-latest 18 | timeout-minutes: 10 19 | 20 | steps: 21 | - uses: actions/checkout@v6 22 | - uses: pnpm/action-setup@v4 23 | - uses: actions/setup-node@v6 24 | with: 25 | node-version: 22 26 | cache: pnpm 27 | - name: Install Dependencies 28 | run: pnpm install --frozen-lockfile 29 | - name: Lint 30 | run: pnpm lint 31 | 32 | test: 33 | name: "Tests" 34 | runs-on: ubuntu-latest 35 | timeout-minutes: 10 36 | outputs: 37 | matrix: ${{ steps.set-matrix.outputs.matrix }} 38 | 39 | steps: 40 | - uses: actions/checkout@v6 41 | - uses: pnpm/action-setup@v4 42 | - uses: actions/setup-node@v6 43 | with: 44 | node-version: 22 45 | cache: pnpm 46 | - name: Install Dependencies 47 | run: pnpm install --frozen-lockfile 48 | - name: Run Tests 49 | run: pnpm test 50 | # For the Try Scenarios 51 | - id: set-matrix 52 | run: | 53 | echo "matrix=$(pnpm -s dlx @embroider/try list)" >> $GITHUB_OUTPUT 54 | 55 | floating: 56 | name: "Floating Dependencies" 57 | runs-on: ubuntu-latest 58 | timeout-minutes: 10 59 | 60 | steps: 61 | - uses: actions/checkout@v6 62 | - uses: pnpm/action-setup@v4 63 | - uses: actions/setup-node@v6 64 | with: 65 | node-version: 22 66 | cache: pnpm 67 | - name: Install Dependencies 68 | run: pnpm install --no-lockfile 69 | - name: Run Tests 70 | run: pnpm test 71 | 72 | try-scenarios: 73 | name: ${{ matrix.name }} 74 | runs-on: ubuntu-latest 75 | needs: "test" 76 | timeout-minutes: 10 77 | strategy: 78 | fail-fast: false 79 | matrix: ${{fromJson(needs.test.outputs.matrix)}} 80 | 81 | steps: 82 | - uses: actions/checkout@v6 83 | - uses: pnpm/action-setup@v4 84 | - uses: actions/setup-node@v6 85 | with: 86 | node-version: 22 87 | cache: pnpm 88 | - name: Apply Scenario 89 | run: | 90 | pnpm dlx @embroider/try apply ${{ matrix.name }} 91 | 92 | - name: Install Dependencies 93 | run: pnpm install --no-lockfile 94 | - name: Run Tests 95 | run: | 96 | pnpm test 97 | 98 | env: ${{ matrix.env }} 99 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Debugging: 3 | * https://eslint.org/docs/latest/use/configure/debug 4 | * ---------------------------------------------------- 5 | * 6 | * Print a file's calculated configuration 7 | * 8 | * npx eslint --print-config path/to/file.js 9 | * 10 | * Inspecting the config 11 | * 12 | * npx eslint --inspect-config 13 | * 14 | */ 15 | 16 | import babelParser from '@babel/eslint-parser/experimental-worker'; 17 | import js from '@eslint/js'; 18 | import { defineConfig, globalIgnores } from 'eslint/config'; 19 | import prettier from 'eslint-config-prettier'; 20 | import ember from 'eslint-plugin-ember/recommended'; 21 | import importPlugin from 'eslint-plugin-import'; 22 | import n from 'eslint-plugin-n'; 23 | import globals from 'globals'; 24 | 25 | const esmParserOptions = { 26 | ecmaFeatures: { modules: true }, 27 | ecmaVersion: 'latest', 28 | }; 29 | 30 | export default defineConfig([ 31 | globalIgnores([ 32 | 'dist/', 33 | 'dist-*/', 34 | 'declarations/', 35 | 'coverage/', 36 | '!**/.*', 37 | 'src/index.d.ts', 38 | ]), 39 | js.configs.recommended, 40 | prettier, 41 | ember.configs.base, 42 | ember.configs.gjs, 43 | /** 44 | * https://eslint.org/docs/latest/use/configure/configuration-files#configuring-linter-options 45 | */ 46 | { 47 | linterOptions: { 48 | reportUnusedDisableDirectives: 'error', 49 | }, 50 | }, 51 | { 52 | files: ['**/*.js'], 53 | languageOptions: { 54 | parser: babelParser, 55 | }, 56 | }, 57 | { 58 | files: ['**/*.{js,gjs}'], 59 | languageOptions: { 60 | parserOptions: esmParserOptions, 61 | globals: { 62 | ...globals.browser, 63 | }, 64 | }, 65 | }, 66 | { 67 | files: ['src/**/*'], 68 | plugins: { 69 | import: importPlugin, 70 | }, 71 | rules: { 72 | // require relative imports use full extensions 73 | 'import/extensions': ['error', 'always', { ignorePackages: true }], 74 | }, 75 | }, 76 | /** 77 | * CJS node files 78 | */ 79 | { 80 | files: ['**/*.cjs'], 81 | plugins: { 82 | n, 83 | }, 84 | 85 | languageOptions: { 86 | sourceType: 'script', 87 | ecmaVersion: 'latest', 88 | globals: { 89 | ...globals.node, 90 | }, 91 | }, 92 | }, 93 | /** 94 | * ESM node files 95 | */ 96 | { 97 | files: ['**/*.mjs'], 98 | plugins: { 99 | n, 100 | }, 101 | 102 | languageOptions: { 103 | sourceType: 'module', 104 | ecmaVersion: 'latest', 105 | parserOptions: esmParserOptions, 106 | globals: { 107 | ...globals.node, 108 | }, 109 | }, 110 | }, 111 | ]); 112 | -------------------------------------------------------------------------------- /tests/unit/deprecation-collector-test.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: 0 */ 2 | 3 | import { module } from 'qunit'; 4 | import test from '../helpers/debug-test'; 5 | import { deprecationCollector } from '#src/index.js'; 6 | 7 | let originalWarn, originalConfig; 8 | 9 | module('deprecationCollector', function (hooks) { 10 | hooks.beforeEach(function () { 11 | originalWarn = console.warn; 12 | 13 | /* 14 | * Clear config for these tests 15 | */ 16 | originalConfig = self.deprecationWorkflow = { 17 | config: null, 18 | deprecationLog: { 19 | messages: new Set(), 20 | }, 21 | }; 22 | }); 23 | 24 | hooks.afterEach(function () { 25 | self.deprecationWorkflow.config = originalConfig; 26 | self.deprecationWorkflow.deprecationLog = { messages: {} }; 27 | console.warn = originalWarn; 28 | }); 29 | 30 | test('it collects deprecations', function (assert) { 31 | deprecationCollector( 32 | 'First deprecation', 33 | { 34 | id: 'first', 35 | since: 'the beginning', 36 | until: 'forever', 37 | for: 'testing', 38 | }, 39 | () => {}, 40 | ); 41 | deprecationCollector( 42 | 'Second deprecation', 43 | { 44 | id: 'second', 45 | since: 'the beginning', 46 | until: 'forever', 47 | for: 'testing', 48 | }, 49 | () => {}, 50 | ); 51 | 52 | assert.deepEqual( 53 | self.deprecationWorkflow.deprecationLog.messages, 54 | new Set(['first', 'second']), 55 | ); 56 | }); 57 | 58 | test('should call next', function (assert) { 59 | assert.expect(1); 60 | 61 | function next() { 62 | assert.ok(true, 'next has been called'); 63 | } 64 | 65 | deprecationCollector( 66 | 'First deprecation', 67 | { 68 | id: 'first', 69 | since: 'the beginning', 70 | until: 'forever', 71 | for: 'testing', 72 | }, 73 | next, 74 | ); 75 | }); 76 | 77 | test('deprecations are not duplicated', function (assert) { 78 | deprecationCollector( 79 | 'First deprecation', 80 | { 81 | id: 'first', 82 | since: 'the beginning', 83 | until: 'forever', 84 | for: 'testing', 85 | }, 86 | () => {}, 87 | ); 88 | deprecationCollector( 89 | 'Second deprecation', 90 | { 91 | id: 'second', 92 | since: 'the beginning', 93 | until: 'forever', 94 | for: 'testing', 95 | }, 96 | () => {}, 97 | ); 98 | 99 | // do it again 100 | deprecationCollector( 101 | 'First deprecation', 102 | { 103 | id: 'first', 104 | since: 'the beginning', 105 | until: 'forever', 106 | for: 'testing', 107 | }, 108 | () => {}, 109 | ); 110 | deprecationCollector( 111 | 'Second deprecation', 112 | { 113 | id: 'second', 114 | since: 'the beginning', 115 | until: 'forever', 116 | for: 'testing', 117 | }, 118 | () => {}, 119 | ); 120 | 121 | assert.deepEqual( 122 | self.deprecationWorkflow.deprecationLog.messages, 123 | new Set(['first', 'second']), 124 | ); 125 | }); 126 | }); 127 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-cli-deprecation-workflow", 3 | "version": "4.0.0", 4 | "description": "Provides a much needed workflow to managing deprecations.", 5 | "keywords": [ 6 | "ember-addon" 7 | ], 8 | "repository": "https://github.com/ember-cli/ember-cli-deprecation-workflow/", 9 | "license": "MIT", 10 | "author": "", 11 | "imports": { 12 | "#src/*": "./src/*" 13 | }, 14 | "exports": { 15 | ".": { 16 | "types": "./src/index.d.ts", 17 | "default": "./src/index.js" 18 | }, 19 | "./addon-main.js": "./addon-main.cjs" 20 | }, 21 | "files": [ 22 | "addon-main.cjs", 23 | "src" 24 | ], 25 | "scripts": { 26 | "format": "prettier . --cache --write", 27 | "lint": "concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\" --prefixColors auto", 28 | "lint:fix": "concurrently \"pnpm:lint:*:fix\" --names \"fix:\" --prefixColors auto && pnpm run format", 29 | "lint:format": "prettier . --cache --check", 30 | "lint:js": "eslint . --cache", 31 | "lint:js:fix": "eslint . --fix", 32 | "start": "vite dev", 33 | "test": "vite build --mode=development --out-dir dist-tests && testem --file testem.cjs ci --port 0" 34 | }, 35 | "dependencies": { 36 | "@embroider/addon-shim": "^1.8.9", 37 | "decorator-transforms": "^2.2.2" 38 | }, 39 | "devDependencies": { 40 | "@babel/core": "^7.25.2", 41 | "@babel/eslint-parser": "^7.25.1", 42 | "@babel/runtime": "^7.25.6", 43 | "@ember/app-tsconfig": "^2.0.0", 44 | "@ember/library-tsconfig": "^2.0.0", 45 | "@ember/test-helpers": "^5.2.1", 46 | "@ember/test-waiters": "^4.1.1", 47 | "@embroider/compat": "^4.1.0", 48 | "@embroider/core": "^4.1.0", 49 | "@embroider/macros": "^1.18.0", 50 | "@embroider/vite": "^1.1.5", 51 | "@eslint/js": "^9.17.0", 52 | "@glimmer/component": "^2.0.0", 53 | "@rollup/plugin-babel": "^6.0.4", 54 | "babel-plugin-ember-template-compilation": "^3.0.1", 55 | "concurrently": "^9.0.1", 56 | "ember-page-title": "^9.0.3", 57 | "ember-qunit": "^9.0.2", 58 | "ember-source": "^6.7.0", 59 | "ember-strict-application-resolver": "^0.1.0", 60 | "eslint": "^9.17.0", 61 | "eslint-config-prettier": "^10.1.5", 62 | "eslint-plugin-ember": "^12.3.3", 63 | "eslint-plugin-import": "^2.31.0", 64 | "eslint-plugin-n": "^17.15.1", 65 | "globals": "^16.1.0", 66 | "prettier": "^3.4.2", 67 | "prettier-plugin-ember-template-tag": "^2.0.4", 68 | "qunit": "^2.24.1", 69 | "qunit-dom": "^3.4.0", 70 | "release-plan": "^0.17.0", 71 | "testem": "^3.15.1", 72 | "vite": "^7.1.9" 73 | }, 74 | "packageManager": "pnpm@10.26.0", 75 | "volta": { 76 | "node": "22", 77 | "pnpm": "10.26.0" 78 | }, 79 | "publishConfig": { 80 | "registry": "https://registry.npmjs.org" 81 | }, 82 | "changelog": { 83 | "repo": "mixonic/ember-cli-deprecation-workflow", 84 | "labels": { 85 | "breaking": ":boom: Breaking Change", 86 | "enhancement": ":rocket: Enhancement", 87 | "bug": ":bug: Bug Fix", 88 | "documentation": ":memo: Documentation", 89 | "internal": ":house: Internal" 90 | } 91 | }, 92 | "ember": { 93 | "edition": "octane" 94 | }, 95 | "ember-addon": { 96 | "version": 2, 97 | "type": "addon", 98 | "main": "addon-main.cjs" 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import { registerDeprecationHandler } from '@ember/debug'; 2 | 3 | const LOG_LIMIT = 100; 4 | 5 | export default function setupDeprecationWorkflow(config) { 6 | self.deprecationWorkflow = self.deprecationWorkflow || {}; 7 | self.deprecationWorkflow.deprecationLog = { 8 | messages: new Set(), 9 | }; 10 | 11 | registerDeprecationHandler((message, options, next) => 12 | handleDeprecationWorkflow(config, message, options, next), 13 | ); 14 | 15 | registerDeprecationHandler(deprecationCollector); 16 | 17 | self.deprecationWorkflow.flushDeprecations = (options) => 18 | flushDeprecations({ config, ...options }); 19 | } 20 | 21 | function matchesWorkflow(matcher, value) { 22 | return ( 23 | (typeof matcher === 'string' && matcher === value) || 24 | (matcher instanceof RegExp && matcher.exec(value)) 25 | ); 26 | } 27 | 28 | export function detectWorkflow(config, message, options) { 29 | if (!config || !config.workflow) { 30 | return; 31 | } 32 | 33 | let i, workflow, matcher, idMatcher; 34 | for (i = 0; i < config.workflow.length; i++) { 35 | workflow = config.workflow[i]; 36 | matcher = workflow.matchMessage; 37 | idMatcher = workflow.matchId; 38 | 39 | if ( 40 | matchesWorkflow(idMatcher, options?.id) || 41 | matchesWorkflow(matcher, message) 42 | ) { 43 | return workflow; 44 | } 45 | } 46 | } 47 | 48 | export function flushDeprecations({ handler = 'silence', config = {} } = {}) { 49 | let messages = self.deprecationWorkflow.deprecationLog.messages; 50 | let existing = config.workflow ?? []; 51 | let collected = messages 52 | .values() 53 | .filter((matchId) => !existing.some((entry) => entry.matchId === matchId)) 54 | .map((matchId) => ({ 55 | handler, 56 | matchId, 57 | })); 58 | 59 | let mergedConfig = { 60 | ...config, 61 | workflow: [...existing, ...collected], 62 | }; 63 | 64 | return `import setupDeprecationWorkflow from 'ember-cli-deprecation-workflow'; 65 | 66 | setupDeprecationWorkflow(${JSON.stringify(mergedConfig, undefined, 2)});`; 67 | } 68 | 69 | export function handleDeprecationWorkflow(config, message, options, next) { 70 | let matchingWorkflow = detectWorkflow(config, message, options); 71 | if (!matchingWorkflow) { 72 | if (config && config.throwOnUnhandled) { 73 | throw new Error(message); 74 | } else { 75 | next(message, options); 76 | } 77 | } else { 78 | switch (matchingWorkflow.handler) { 79 | case 'silence': 80 | // no-op 81 | break; 82 | case 'log': { 83 | let key = (options && options.id) || message; 84 | 85 | if (!self.deprecationWorkflow.logCounts) { 86 | self.deprecationWorkflow.logCounts = {}; 87 | } 88 | 89 | let count = self.deprecationWorkflow.logCounts[key] || 0; 90 | self.deprecationWorkflow.logCounts[key] = ++count; 91 | 92 | if (count <= LOG_LIMIT) { 93 | console.warn('DEPRECATION: ' + message); 94 | if (count === LOG_LIMIT) { 95 | console.warn( 96 | 'To avoid console overflow, this deprecation will not be logged any more in this run.', 97 | ); 98 | } 99 | } 100 | 101 | break; 102 | } 103 | case 'throw': 104 | throw new Error(message + ` (id: ${options?.id || 'unknown'})`); 105 | default: 106 | next(message, options); 107 | break; 108 | } 109 | } 110 | } 111 | 112 | export function deprecationCollector(message, options, next) { 113 | self.deprecationWorkflow.deprecationLog.messages.add(options.id); 114 | 115 | next(message, options); 116 | } 117 | -------------------------------------------------------------------------------- /tests/unit/flush-deprecations-test.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: 0 */ 2 | 3 | import { module } from 'qunit'; 4 | import test from '../helpers/debug-test'; 5 | import { flushDeprecations } from '#src/index.js'; 6 | 7 | let originalWarn, originalConfig; 8 | 9 | module('flushDeprecations', function (hooks) { 10 | hooks.beforeEach(function () { 11 | originalWarn = console.warn; 12 | 13 | /* 14 | * Clear config for these tests 15 | */ 16 | originalConfig = self.deprecationWorkflow = { 17 | config: null, 18 | deprecationLog: { 19 | messages: new Set(), 20 | }, 21 | }; 22 | }); 23 | 24 | hooks.afterEach(function () { 25 | self.deprecationWorkflow.config = originalConfig; 26 | self.deprecationWorkflow.deprecationLog = { messages: new Set() }; 27 | console.warn = originalWarn; 28 | }); 29 | 30 | test('calling flushDeprecations returns workflow config', function (assert) { 31 | self.deprecationWorkflow.deprecationLog.messages = new Set([ 32 | 'first', 33 | 'second', 34 | ]); 35 | 36 | let deprecationsPayload = flushDeprecations(); 37 | let expectedConfig = { 38 | workflow: [ 39 | { handler: 'silence', matchId: 'first' }, 40 | { handler: 'silence', matchId: 'second' }, 41 | ], 42 | }; 43 | 44 | assert.strictEqual( 45 | deprecationsPayload, 46 | `import setupDeprecationWorkflow from 'ember-cli-deprecation-workflow'; 47 | 48 | setupDeprecationWorkflow(${JSON.stringify(expectedConfig, undefined, 2)});`, 49 | ); 50 | }); 51 | 52 | test('calling flushDeprecations with custom handler returns workflow config', function (assert) { 53 | self.deprecationWorkflow.deprecationLog.messages = new Set([ 54 | 'first', 55 | 'second', 56 | ]); 57 | 58 | let deprecationsPayload = flushDeprecations({ handler: 'log' }); 59 | let expectedConfig = { 60 | workflow: [ 61 | { handler: 'log', matchId: 'first' }, 62 | { handler: 'log', matchId: 'second' }, 63 | ], 64 | }; 65 | 66 | assert.strictEqual( 67 | deprecationsPayload, 68 | `import setupDeprecationWorkflow from 'ember-cli-deprecation-workflow'; 69 | 70 | setupDeprecationWorkflow(${JSON.stringify(expectedConfig, undefined, 2)});`, 71 | ); 72 | }); 73 | 74 | test('calling flushDeprecations with existing config and no deprecations returns original config', function (assert) { 75 | let config = { 76 | throwOnUnhandled: true, 77 | workflow: [{ handler: 'log', matchId: 'existing' }], 78 | }; 79 | self.deprecationWorkflow.deprecationLog.messages = new Set([]); 80 | 81 | let deprecationsPayload = flushDeprecations({ config }); 82 | assert.strictEqual( 83 | deprecationsPayload, 84 | `import setupDeprecationWorkflow from 'ember-cli-deprecation-workflow'; 85 | 86 | setupDeprecationWorkflow(${JSON.stringify(config, undefined, 2)});`, 87 | ); 88 | }); 89 | 90 | test('calling flushDeprecations with existing config returns augmented config', function (assert) { 91 | let config = { 92 | throwOnUnhandled: true, 93 | workflow: [{ handler: 'log', matchId: 'existing' }], 94 | }; 95 | self.deprecationWorkflow.deprecationLog.messages = new Set([ 96 | 'first', 97 | 'second', 98 | ]); 99 | 100 | let deprecationsPayload = flushDeprecations({ config }); 101 | let expectedConfig = { 102 | throwOnUnhandled: true, 103 | workflow: [ 104 | { handler: 'log', matchId: 'existing' }, 105 | { handler: 'silence', matchId: 'first' }, 106 | { handler: 'silence', matchId: 'second' }, 107 | ], 108 | }; 109 | assert.strictEqual( 110 | deprecationsPayload, 111 | `import setupDeprecationWorkflow from 'ember-cli-deprecation-workflow'; 112 | 113 | setupDeprecationWorkflow(${JSON.stringify(expectedConfig, undefined, 2)});`, 114 | ); 115 | }); 116 | 117 | test('calling flushDeprecations with existing config does not override existing deprecations', function (assert) { 118 | let config = { 119 | throwOnUnhandled: true, 120 | workflow: [{ handler: 'log', matchId: 'existing' }], 121 | }; 122 | self.deprecationWorkflow.deprecationLog.messages = new Set([ 123 | 'first', 124 | 'second', 125 | 'existing', 126 | ]); 127 | 128 | let deprecationsPayload = flushDeprecations({ config }); 129 | let expectedConfig = { 130 | throwOnUnhandled: true, 131 | workflow: [ 132 | { handler: 'log', matchId: 'existing' }, 133 | { handler: 'silence', matchId: 'first' }, 134 | { handler: 'silence', matchId: 'second' }, 135 | ], 136 | }; 137 | assert.strictEqual( 138 | deprecationsPayload, 139 | `import setupDeprecationWorkflow from 'ember-cli-deprecation-workflow'; 140 | 141 | setupDeprecationWorkflow(${JSON.stringify(expectedConfig, undefined, 2)});`, 142 | ); 143 | }); 144 | }); 145 | -------------------------------------------------------------------------------- /tests/acceptance/workflow-config-test.js: -------------------------------------------------------------------------------- 1 | import { deprecate } from '@ember/debug'; 2 | import { module } from 'qunit'; 3 | import test from '../helpers/debug-test'; 4 | 5 | let originalWarn; 6 | 7 | module('workflow config', function (hooks) { 8 | hooks.beforeEach(function () { 9 | originalWarn = window.Testem.handleConsoleMessage; 10 | }); 11 | 12 | hooks.afterEach(function () { 13 | window.deprecationWorkflow.deprecationLog = { messages: new Set() }; 14 | window.Testem.handleConsoleMessage = originalWarn; 15 | }); 16 | 17 | test('deprecation silenced with string matcher', function (assert) { 18 | deprecate('silence-strict', false, { 19 | since: '2.0.0', 20 | until: 'forever', 21 | id: 'test', 22 | for: 'testing', 23 | }); 24 | assert.ok(true, 'Deprecation did not raise'); 25 | }); 26 | 27 | test('deprecation logs with message matcher', function (assert) { 28 | assert.expect(1); 29 | 30 | let message = 'log-strict'; 31 | window.Testem.handleConsoleMessage = function (passedMessage) { 32 | assert.strictEqual( 33 | passedMessage.indexOf('DEPRECATION: ' + message), 34 | 0, 35 | 'deprecation logs', 36 | ); 37 | }; 38 | deprecate(message, false, { 39 | since: '2.0.0', 40 | until: 'forever', 41 | id: 'test', 42 | for: 'testing', 43 | }); 44 | }); 45 | 46 | test('deprecation logs with message matcher by regex', function (assert) { 47 | assert.expect(1); 48 | 49 | let message = ' foo log-match foo'; 50 | window.Testem.handleConsoleMessage = function (passedMessage) { 51 | assert.strictEqual( 52 | passedMessage.indexOf('DEPRECATION: ' + message), 53 | 0, 54 | 'deprecation logs', 55 | ); 56 | }; 57 | deprecate(message, false, { 58 | since: 'now', 59 | until: 'forever', 60 | id: 'test', 61 | for: 'testing', 62 | }); 63 | }); 64 | 65 | test('deprecation logs with id matcher', function (assert) { 66 | assert.expect(1); 67 | 68 | let message = ' foo foo'; 69 | window.Testem.handleConsoleMessage = function (passedMessage) { 70 | assert.strictEqual( 71 | passedMessage.indexOf('DEPRECATION: ' + message), 72 | 0, 73 | 'deprecation logs', 74 | ); 75 | }; 76 | deprecate(message, false, { 77 | since: 'now', 78 | until: 'forever', 79 | id: 'log-strict', 80 | for: 'testing', 81 | }); 82 | }); 83 | 84 | test('deprecation thrown with string matcher', function (assert) { 85 | assert.throws(function () { 86 | deprecate('throw-strict', false, { 87 | since: '2.0.0', 88 | until: 'forever', 89 | id: 'test', 90 | for: 'testing', 91 | }); 92 | }, 'deprecation throws'); 93 | }); 94 | 95 | test('deprecation logs with id matcher and options', function (assert) { 96 | assert.expect(1); 97 | 98 | let message = 'arbitrary-unmatched-message'; 99 | let id = 'log-strict'; 100 | let options = { 101 | id, 102 | since: '2.0.0', 103 | until: '3.0.0', 104 | for: 'testing', 105 | }; 106 | let expected = `DEPRECATION: ${message}`; 107 | window.Testem.handleConsoleMessage = function (passedMessage) { 108 | assert.strictEqual( 109 | passedMessage.substr(0, expected.length), 110 | expected, 111 | 'deprecation logs', 112 | ); 113 | }; 114 | deprecate(message, false, options); 115 | }); 116 | 117 | test('deprecation limits each id to 100 console.logs', function (assert) { 118 | assert.expect(104); 119 | let limit = 100; 120 | 121 | let message = 'log-match'; 122 | let id = 'first-and-unique-to-limit-test'; 123 | let options = { 124 | id, 125 | since: '2.0.0', 126 | until: '3.0.0', 127 | for: 'testing', 128 | }; 129 | let expected = `DEPRECATION: ${message}`; 130 | 131 | let count = 0; 132 | window.Testem.handleConsoleMessage = function (passedMessage) { 133 | count++; 134 | if (count <= limit) { 135 | assert.strictEqual( 136 | passedMessage.substr(0, expected.length), 137 | expected, 138 | 'deprecation logs', 139 | ); 140 | } 141 | if (count === limit) { 142 | window.Testem.handleConsoleMessage = function (passedMessage) { 143 | assert.strictEqual( 144 | passedMessage, 145 | 'To avoid console overflow, this deprecation will not be logged any more in this run.', 146 | ); 147 | }; 148 | } 149 | }; 150 | 151 | // Run one more time than the limit 152 | for (let i = 0; i <= limit; i++) { 153 | deprecate(message, false, options); 154 | } 155 | 156 | assert.strictEqual( 157 | count, 158 | limit, 159 | 'logged 100 times, including final notice', 160 | ); 161 | 162 | let secondMessage = 'log-strict'; 163 | let secondId = 'second-and-unique-to-limit-test'; 164 | let secondOptions = { 165 | id: secondId, 166 | since: '2.0.0', 167 | until: '3.0.0', 168 | for: 'testing', 169 | }; 170 | let secondExpected = `DEPRECATION: ${secondMessage}`; 171 | 172 | let secondCount = 0; 173 | window.Testem.handleConsoleMessage = function (passedMessage) { 174 | secondCount++; 175 | assert.strictEqual( 176 | passedMessage.substr(0, secondExpected.length), 177 | secondExpected, 178 | 'second deprecation logs', 179 | ); 180 | window.Testem.handleConsoleMessage = function () { 181 | assert.ok(false, 'No further logging expected'); 182 | }; 183 | }; 184 | 185 | deprecate(secondMessage, false, secondOptions); 186 | 187 | assert.strictEqual(secondCount, 1, 'logged deprecation with different id'); 188 | }); 189 | }); 190 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ember-cli-deprecation-workflow 2 | 3 | An addon geared towards making Ember upgrades easier by allowing you to work 4 | through deprecations without massive console noise. 5 | 6 | ## History 7 | 8 | Upgrading Ember versions can be very daunting. One of the largest factors is the 9 | massive `console.log` noise that the deprecations introduced in those versions 10 | (to help us know what we need to do to stay up to date) is so overwhelming that 11 | we quite literally have no idea what to do. 12 | 13 | The "deprecation spew" issue became very obvious as we progressed into the later 14 | 1.13 beta releases. At that point, [@mixonic](https://twitter.com/mixonic) and 15 | [@rwjblue](https://twitter.com/rwjblue) came up with a wild scheme. 16 | 17 | The scheme was to build tooling which made dealing with deprecations an 18 | incremental process. ember-cli-deprecation-workflow allows you to focus on 19 | addressing a single deprecation at a time, and prevents backsliding 20 | (re-introduction of a deprecated API use) in a codebase. 21 | 22 | ## Usage 23 | 24 | ### Compatibility 25 | 26 | 3.x 27 | 28 | - Ember.js 3.28 until at least 5.4 29 | - Ember CLI 4.12 or above 30 | - Node.js 16 or above 31 | 32 | 2.x 33 | 34 | - Ember.js 2.12 until at least 4.12 35 | - Ember CLI 3.16 or above 36 | - Node.js 12 and 14 or above 37 | 38 | 1.x 39 | 40 | - Ember.js 1.13 until at least 3.4 41 | - Ember CLI 3.4 as well as many versions before and after 42 | - Node.js 6, 8, and 10 until at least 14 43 | 44 | ### Getting started 45 | 46 | 1. Install the ember-cli-deprecation-workflow addon (`ember install ember-cli-deprecation-workflow`). 47 | 2. Create an `app/deprecation-workflow.js` file with the following content: 48 | 49 | ```js 50 | import setupDeprecationWorkflow from 'ember-cli-deprecation-workflow'; 51 | 52 | setupDeprecationWorkflow(); 53 | ``` 54 | 55 | 3. In your `app/app.js`, do: 56 | 57 | ```js 58 | import './deprecation-workflow'; 59 | ``` 60 | 61 | 4. Run your test suite\* with `ember test --server`. 62 | 5. Navigate to your tests (default: http://localhost:7357/) 63 | 6. Run `deprecationWorkflow.flushDeprecations()` in your browsers console. Or `flushDeprecations({ handler: 'log' })` if you want a different [handler](#handlers) than the default of `silence`. 64 | 7. Copy the string output and overwrite the content of `app/deprecation-workflow.js`. 65 | 66 | In Chrome, use right click → "Copy string contents" to avoid escape characters. 67 | 68 | Once this initial setup is completed the "deprecation spew" should be largely 69 | "fixed". Only unhandled deprecations will be displayed in your console. 70 | 71 | \*Note: Unless your test coverage is amazing (>90%), it's likely that running 72 | the test suite alone will not reveal _every_ deprecation. It may be prudent to 73 | run through the app's workflows live and flush deprecations a second time, 74 | merging the resulting output list with that generated from your test suite. 75 | 76 | Now that the spew has settled down, you can process one deprecation at a time while ensuring that no new deprecations are introduced. 77 | 78 | ### Workflow 79 | 80 | What does that individual deprecation workflow look like? 81 | 82 | 1. Change one entry in `app/deprecation-workflow.js` from `silence` to `throw`. 83 | 2. Run your tests or use your application. 84 | 3. Errors will be thrown for just that one deprecation, and you can track down the fixes needed in relative isolation of the rest of the deprecations. 85 | 4. Once the deprecation has been dealt with, remove its entry from `app/deprecation-workflow.js`. 86 | 5. Lather and repeat. 87 | 88 | ### Handlers 89 | 90 | There are 3 defined handlers that have different behaviors 91 | 92 | | Handler | Behavior | 93 | | --------- | ---------------------------------------------------------------------------------------------------------------- | 94 | | `silence` | Keeps this deprecation from spewing all over the console | 95 | | `log` | Normal deprecation behavior runs for this deprecation and messages are logged to the console | 96 | | `throw` | The error is thrown instead of allowing the deprecated behavior to run. **_WARNING: APPLICATION MAY GO :boom:_** | 97 | 98 | ### Matchers 99 | 100 | the output from running `deprecationWorkflow.flushDeprecations()` gives you a 101 | nice Json like JS object with all the deprecations in your app. The 102 | `matchMessage` property determines what to filter out of the console. You can 103 | pass a string that must match the console message exactly or a `RegExp` for 104 | `ember-cli-deprecation-workflow` filter the log by. 105 | 106 | ### Production builds 107 | 108 | By default, production ember-cli builds already remove deprecation warnings. Any 109 | deprecations configured to `throw` or `log` will only do so in non-production 110 | builds. 111 | 112 | ### Enable / Disable through configuration 113 | 114 | If your app has disabled test files in development environment you can force enabling this addon through configuration in `ember-cli-build.js` instead: 115 | ```javascript 116 | 'ember-cli-deprecation-workflow': { 117 | enabled: true, 118 | }, 119 | ``` 120 | 121 | ### Catch-all 122 | 123 | To force all deprecations to throw (can be useful in larger teams to prevent 124 | accidental introduction of deprecations), update your 125 | `app/deprecation-workflow.js`: 126 | 127 | ```javascript 128 | window.deprecationWorkflow.config = { 129 | throwOnUnhandled: true, 130 | }; 131 | ``` 132 | 133 | ### Template Deprecations 134 | 135 | By default, the console based deprecations that occur during template 136 | compilation are suppressed in favor of browser deprecations ran during the test 137 | suite. If you would prefer to still have the deprecations in the console, add 138 | the following to your `app/environment.js`: 139 | 140 | ```javascript 141 | module.exports = function (env) { 142 | var ENV = {}; 143 | 144 | // normal things here 145 | 146 | ENV.logTemplateLintToConsole = true; 147 | }; 148 | ``` 149 | 150 | ### Configuration 151 | 152 | In some cases, it may be necessary to indicate a different `config` directory 153 | from the default one (`/config`). For example, you may want the flushed 154 | deprecations file to be referenced in a config directory like `my-config`. 155 | 156 | Adjust the `configPath` in your `package.json` file. The `/` will automatically 157 | be prefixed. 158 | 159 | ```javascript 160 | { 161 | 'ember-addon': { 162 | configPath: 'my-config' 163 | } 164 | } 165 | ``` 166 | 167 | ## Contributing 168 | 169 | Details on contributing to the addon itself (not required for normal usage). 170 | 171 | See the [Contributing](CONTRIBUTING.md) guide for details. 172 | 173 | ## License 174 | 175 | This project is licensed under the [MIT License](LICENSE.md). 176 | -------------------------------------------------------------------------------- /tests/unit/handle-deprecation-workflow-test.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console: 0 */ 2 | 3 | import { module } from 'qunit'; 4 | import test from '../helpers/debug-test'; 5 | import { handleDeprecationWorkflow } from '#src/index.js'; 6 | 7 | let originalWarn, originalConfig; 8 | 9 | module('handleDeprecationWorkflow', function (hooks) { 10 | hooks.beforeEach(function () { 11 | originalWarn = console.warn; 12 | 13 | /* 14 | * Clear config for these tests 15 | */ 16 | originalConfig = self.deprecationWorkflow = { 17 | config: null, 18 | deprecationLog: { 19 | messages: {}, 20 | }, 21 | }; 22 | }); 23 | 24 | hooks.afterEach(function () { 25 | self.deprecationWorkflow.config = originalConfig; 26 | self.deprecationWorkflow.deprecationLog = { messages: {} }; 27 | console.warn = originalWarn; 28 | }); 29 | 30 | test('specifying `throwOnUnhandled` as true raises', function (assert) { 31 | const config = { 32 | throwOnUnhandled: true, 33 | workflow: [{ handler: 'silence', matchMessage: 'Sshhhhh!!' }], 34 | }; 35 | 36 | assert.throws( 37 | function () { 38 | handleDeprecationWorkflow( 39 | config, 40 | 'Foobarrrzzzz', 41 | { 42 | since: 'the beginning', 43 | until: 'forever', 44 | id: 'foobar', 45 | for: 'testing', 46 | }, 47 | () => {}, 48 | ); 49 | }, 50 | /Foobarrrzzzz/, 51 | 'setting raiseOnUnhandled throws for unknown workflows', 52 | ); 53 | 54 | handleDeprecationWorkflow( 55 | config, 56 | 'Sshhhhh!!', 57 | { 58 | id: 'quiet', 59 | since: 'the beginning', 60 | until: 'forever', 61 | for: 'testing', 62 | }, 63 | () => {}, 64 | ); 65 | assert.ok(true, 'did not throw when silenced'); 66 | }); 67 | 68 | test('specifying `throwOnUnhandled` as false does nothing', function (assert) { 69 | const config = { 70 | throwOnUnhandled: false, 71 | }; 72 | 73 | handleDeprecationWorkflow( 74 | config, 75 | 'Sshhhhh!!', 76 | { 77 | id: 'quiet', 78 | since: 'the beginning', 79 | until: 'forever', 80 | for: 'testing', 81 | }, 82 | () => {}, 83 | ); 84 | 85 | assert.ok(true, 'does not die when throwOnUnhandled is false'); 86 | }); 87 | 88 | test('deprecation silenced with string matcher', function (assert) { 89 | const config = { 90 | throwOnUnhandled: true, 91 | workflow: [{ matchMessage: 'Interesting', handler: 'silence' }], 92 | }; 93 | 94 | handleDeprecationWorkflow(config, 'Interesting', { 95 | id: 'interesting', 96 | since: 'the beginning', 97 | until: 'forever', 98 | for: 'testing', 99 | }); 100 | assert.ok(true, 'Deprecation did not raise'); 101 | }); 102 | 103 | test('deprecation logs with string matcher', function (assert) { 104 | assert.expect(1); 105 | 106 | let message = 'Interesting'; 107 | console.warn = function (passedMessage) { 108 | assert.strictEqual( 109 | passedMessage.indexOf('DEPRECATION: ' + message), 110 | 0, 111 | 'deprecation logs', 112 | ); 113 | }; 114 | 115 | const config = { 116 | throwOnUnhandled: true, 117 | workflow: [{ matchMessage: message, handler: 'log' }], 118 | }; 119 | 120 | handleDeprecationWorkflow( 121 | config, 122 | message, 123 | { 124 | since: 'the beginning', 125 | until: 'forever', 126 | id: 'interesting', 127 | for: 'testing', 128 | }, 129 | () => {}, 130 | ); 131 | }); 132 | 133 | test('deprecation thrown with string matcher', function (assert) { 134 | const config = { 135 | workflow: [{ matchMessage: 'Interesting', handler: 'throw' }], 136 | }; 137 | 138 | assert.throws(function () { 139 | handleDeprecationWorkflow( 140 | config, 141 | 'Interesting', 142 | { 143 | id: 'interesting', 144 | since: 'the beginning', 145 | until: 'forever', 146 | for: 'testing', 147 | }, 148 | () => {}, 149 | ); 150 | }, 'deprecation throws'); 151 | }); 152 | 153 | test('deprecation silenced with regex matcher', function (assert) { 154 | const config = { 155 | throwOnUnhandled: true, 156 | workflow: [{ matchMessage: /Inter/, handler: 'silence' }], 157 | }; 158 | 159 | handleDeprecationWorkflow( 160 | config, 161 | 'Interesting', 162 | { 163 | id: 'interesting', 164 | since: 'the beginning', 165 | until: 'forever', 166 | for: 'testing', 167 | }, 168 | () => {}, 169 | ); 170 | 171 | assert.ok(true, 'Deprecation did not raise'); 172 | }); 173 | 174 | test('deprecation logs with regex matcher', function (assert) { 175 | assert.expect(1); 176 | 177 | let message = 'Interesting'; 178 | 179 | console.warn = function (passedMessage) { 180 | assert.strictEqual( 181 | passedMessage, 182 | 'DEPRECATION: ' + message, 183 | 'deprecation logs', 184 | ); 185 | }; 186 | 187 | const config = { 188 | throwOnUnhandled: true, 189 | workflow: [{ matchMessage: /Inter/, handler: 'log' }], 190 | }; 191 | 192 | handleDeprecationWorkflow( 193 | config, 194 | message, 195 | { 196 | id: 'interesting', 197 | since: 'the beginning', 198 | until: 'forever', 199 | for: 'testing', 200 | }, 201 | () => {}, 202 | ); 203 | }); 204 | 205 | test('deprecation thrown with regex matcher', function (assert) { 206 | const config = { 207 | workflow: [{ matchMessage: /Inter/, handler: 'throw' }], 208 | }; 209 | 210 | assert.throws(function () { 211 | handleDeprecationWorkflow( 212 | config, 213 | 'Interesting', 214 | { 215 | id: 'interesting', 216 | since: 'the beginning', 217 | until: 'forever', 218 | for: 'testing', 219 | }, 220 | () => {}, 221 | ); 222 | }, 'deprecation throws'); 223 | }); 224 | 225 | test('deprecation thrown with string matcher with parens', function (assert) { 226 | let message = 227 | 'Some string that includes (). If treated like a regexp this will not match.'; 228 | 229 | const config = { 230 | workflow: [{ matchMessage: message, handler: 'throw' }], 231 | }; 232 | 233 | assert.throws(function () { 234 | handleDeprecationWorkflow( 235 | config, 236 | message, 237 | { 238 | id: 'throws', 239 | since: 'the beginning', 240 | until: 'forever', 241 | for: 'testing', 242 | }, 243 | () => {}, 244 | ); 245 | }, 'deprecation throws'); 246 | }); 247 | 248 | test('deprecation silenced with id matcher', function (assert) { 249 | const config = { 250 | throwOnUnhandled: true, 251 | workflow: [{ matchId: 'ember.deprecation-workflow', handler: 'silence' }], 252 | }; 253 | 254 | handleDeprecationWorkflow( 255 | config, 256 | 'Slightly interesting', 257 | { 258 | id: 'ember.deprecation-workflow', 259 | since: 'the beginning', 260 | until: '3.0.0', 261 | for: 'testing', 262 | }, 263 | () => {}, 264 | ); 265 | 266 | assert.ok(true, 'Deprecation did not raise'); 267 | }); 268 | 269 | test('deprecation logs with id matcher', function (assert) { 270 | assert.expect(1); 271 | 272 | let message = 'Slightly interesting'; 273 | 274 | console.warn = function (passedMessage) { 275 | assert.strictEqual( 276 | passedMessage, 277 | 'DEPRECATION: ' + message, 278 | 'deprecation logs', 279 | ); 280 | }; 281 | 282 | const config = { 283 | throwOnUnhandled: true, 284 | workflow: [{ matchId: 'ember.deprecation-workflow', handler: 'log' }], 285 | }; 286 | 287 | handleDeprecationWorkflow( 288 | config, 289 | 'Slightly interesting', 290 | { 291 | id: 'ember.deprecation-workflow', 292 | since: 'the beginning', 293 | until: '3.0.0', 294 | for: 'testing', 295 | }, 296 | () => {}, 297 | ); 298 | }); 299 | 300 | test('deprecation thrown with id matcher', function (assert) { 301 | const config = { 302 | workflow: [{ matchId: 'ember.deprecation-workflow', handler: 'throw' }], 303 | }; 304 | assert.throws(function () { 305 | handleDeprecationWorkflow( 306 | config, 307 | 'Slightly interesting', 308 | { 309 | id: 'ember.deprecation-workflow', 310 | since: 'the beginning', 311 | until: '3.0.0', 312 | for: 'testing', 313 | }, 314 | () => {}, 315 | ); 316 | }, 'deprecation throws'); 317 | }); 318 | 319 | test('deprecation silenced with id regex', function (assert) { 320 | const config = { 321 | throwOnUnhandled: true, 322 | workflow: [{ matchId: /^ember\..*/, handler: 'silence' }], 323 | }; 324 | 325 | handleDeprecationWorkflow( 326 | config, 327 | 'Slightly interesting', 328 | { 329 | id: 'ember.deprecation-workflow', 330 | since: 'the beginning', 331 | until: '3.0.0', 332 | for: 'testing', 333 | }, 334 | () => {}, 335 | ); 336 | 337 | assert.ok(true, 'Deprecation did not raise'); 338 | }); 339 | 340 | test('deprecation logs with id regex', function (assert) { 341 | assert.expect(1); 342 | 343 | let message = 'Slightly interesting'; 344 | 345 | console.warn = function (passedMessage) { 346 | assert.strictEqual( 347 | passedMessage, 348 | 'DEPRECATION: ' + message, 349 | 'deprecation logs', 350 | ); 351 | }; 352 | 353 | const config = { 354 | throwOnUnhandled: true, 355 | workflow: [{ matchId: /^ember\..*/, handler: 'log' }], 356 | }; 357 | 358 | handleDeprecationWorkflow( 359 | config, 360 | 'Slightly interesting', 361 | { 362 | id: 'ember.deprecation-workflow', 363 | since: 'the beginning', 364 | until: '3.0.0', 365 | for: 'testing', 366 | }, 367 | () => {}, 368 | ); 369 | }); 370 | 371 | test('deprecation thrown with id regex', function (assert) { 372 | const config = { 373 | workflow: [{ matchId: /^ember\..*/, handler: 'throw' }], 374 | }; 375 | assert.throws(function () { 376 | handleDeprecationWorkflow( 377 | config, 378 | 'Slightly interesting', 379 | { 380 | id: 'ember.deprecation-workflow', 381 | since: 'the beginning', 382 | until: '3.0.0', 383 | for: 'testing', 384 | }, 385 | () => {}, 386 | ); 387 | }, 'deprecation throws'); 388 | }); 389 | }); 390 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Release (2025-12-18) 4 | 5 | * ember-cli-deprecation-workflow 4.0.0 (major) 6 | 7 | #### :boom: Breaking Change 8 | * `ember-cli-deprecation-workflow` 9 | * [#222](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/222) Convert to v2 Addon - classic projects (not yet using embroider for builds) will want to remain on v3 ([@NullVoxPopuli](https://github.com/NullVoxPopuli)) 10 | 11 | #### :house: Internal 12 | * `ember-cli-deprecation-workflow` 13 | * [#223](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/223) Update release plan ([@NullVoxPopuli](https://github.com/NullVoxPopuli)) 14 | * [#220](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/220) chore: Test latest LTS versions ([@TSenter](https://github.com/TSenter)) 15 | 16 | #### Committers: 2 17 | - Tyler Senter ([@TSenter](https://github.com/TSenter)) 18 | - [@NullVoxPopuli](https://github.com/NullVoxPopuli) 19 | 20 | ## Release (2025-07-03) 21 | 22 | ember-cli-deprecation-workflow 3.4.0 (minor) 23 | 24 | #### :rocket: Enhancement 25 | * `ember-cli-deprecation-workflow` 26 | * [#217](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/217) Throw Deprecation ID ([@jrjohnson](https://github.com/jrjohnson)) 27 | 28 | #### :bug: Bug Fix 29 | * `ember-cli-deprecation-workflow` 30 | * [#215](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/215) Relax ember-source peerDependency for 3.28 support ([@apellerano-pw](https://github.com/apellerano-pw)) 31 | 32 | #### :house: Internal 33 | * `ember-cli-deprecation-workflow` 34 | * [#219](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/219) fix CI for Ember 6.x ([@mansona](https://github.com/mansona)) 35 | 36 | #### Committers: 3 37 | - Andrew Pellerano ([@apellerano-pw](https://github.com/apellerano-pw)) 38 | - Chris Manson ([@mansona](https://github.com/mansona)) 39 | - Jon Johnson ([@jrjohnson](https://github.com/jrjohnson)) 40 | 41 | ## Release (2025-03-21) 42 | 43 | ember-cli-deprecation-workflow 3.3.0 (minor) 44 | 45 | #### :rocket: Enhancement 46 | * `ember-cli-deprecation-workflow` 47 | * [#209](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/209) flushDeprecations merges detected deprecations with existing config ([@simonihmig](https://github.com/simonihmig)) 48 | 49 | #### Committers: 1 50 | - Simon Ihmig ([@simonihmig](https://github.com/simonihmig)) 51 | 52 | ## Release (2025-03-21) 53 | 54 | ember-cli-deprecation-workflow 3.2.1 (patch) 55 | 56 | #### :bug: Bug Fix 57 | * `ember-cli-deprecation-workflow` 58 | * [#212](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/212) Fix type declarations ([@simonihmig](https://github.com/simonihmig)) 59 | * [#210](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/210) Options are optional ([@ef4](https://github.com/ef4)) 60 | 61 | #### Committers: 2 62 | - Edward Faulkner ([@ef4](https://github.com/ef4)) 63 | - Simon Ihmig ([@simonihmig](https://github.com/simonihmig)) 64 | 65 | ## Release (2025-03-06) 66 | 67 | ember-cli-deprecation-workflow 3.2.0 (minor) 68 | 69 | #### :rocket: Enhancement 70 | * `ember-cli-deprecation-workflow` 71 | * [#207](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/207) Allow passing a custom handler to flushDeprecations ([@simonihmig](https://github.com/simonihmig)) 72 | * [#206](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/206) Support RegExp for matchId ([@simonihmig](https://github.com/simonihmig)) 73 | 74 | #### Committers: 1 75 | - Simon Ihmig ([@simonihmig](https://github.com/simonihmig)) 76 | 77 | ## Release (2024-12-27) 78 | 79 | ember-cli-deprecation-workflow 3.1.0 (minor) 80 | 81 | #### :rocket: Enhancement 82 | * `ember-cli-deprecation-workflow` 83 | * [#200](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/200) Add TypeScript declaration file ([@bertdeblock](https://github.com/bertdeblock)) 84 | 85 | #### Committers: 1 86 | - Bert De Block ([@bertdeblock](https://github.com/bertdeblock)) 87 | 88 | ## Release (2024-08-21) 89 | 90 | ember-cli-deprecation-workflow 3.0.2 (patch) 91 | 92 | #### :bug: Bug Fix 93 | * `ember-cli-deprecation-workflow` 94 | * [#197](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/197) Remove @ember/string (it's unused) ([@NullVoxPopuli](https://github.com/NullVoxPopuli)) 95 | 96 | #### Committers: 1 97 | - [@NullVoxPopuli](https://github.com/NullVoxPopuli) 98 | 99 | ## Release (2024-07-11) 100 | 101 | ember-cli-deprecation-workflow 3.0.1 (patch) 102 | 103 | #### :house: Internal 104 | * `ember-cli-deprecation-workflow` 105 | * [#192](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/192) fix repository link in package.json ([@mansona](https://github.com/mansona)) 106 | * [#191](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/191) update release plan workflow ([@mansona](https://github.com/mansona)) 107 | 108 | #### Committers: 1 109 | - Chris Manson ([@mansona](https://github.com/mansona)) 110 | 111 | ## Release (2024-06-25) 112 | 113 | ember-cli-deprecation-workflow 3.0.0 (major) 114 | 115 | #### :boom: Breaking Change 116 | * `ember-cli-deprecation-workflow` 117 | * [#159](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/159) [BREAKING] Convert to a module. Drops support for Ember < 3.28, requires manual initialization ([@lolmaus](https://github.com/lolmaus)) 118 | * [#175](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/175) Node 16 is the minimum supported version ([@mixonic](https://github.com/mixonic)) 119 | 120 | #### :bug: Bug Fix 121 | * `ember-cli-deprecation-workflow` 122 | * [#181](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/181) Remove unused broccoli magic ([@simonihmig](https://github.com/simonihmig)) 123 | 124 | #### :memo: Documentation 125 | * `ember-cli-deprecation-workflow` 126 | * [#184](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/184) Update configuration paths in documentation ([@backspace](https://github.com/backspace)) 127 | 128 | #### :house: Internal 129 | * `ember-cli-deprecation-workflow` 130 | * [#189](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/189) start using release-plan ([@mansona](https://github.com/mansona)) 131 | * [#188](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/188) start using pnpm ([@mansona](https://github.com/mansona)) 132 | * [#178](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/178) Upgrade Ember CLI to 5.4 ([@lolmaus](https://github.com/lolmaus)) 133 | * [#170](https://github.com/ember-cli/ember-cli-deprecation-workflow/pull/170) Bump Node, swap to npm, update CI pipeline ([@mixonic](https://github.com/mixonic)) 134 | 135 | #### Committers: 5 136 | - Andrey Mikhaylov (lolmaus) ([@lolmaus](https://github.com/lolmaus)) 137 | - Buck Doyle ([@backspace](https://github.com/backspace)) 138 | - Chris Manson ([@mansona](https://github.com/mansona)) 139 | - Matthew Beale ([@mixonic](https://github.com/mixonic)) 140 | - Simon Ihmig ([@simonihmig](https://github.com/simonihmig)) 141 | 142 | ## v2.2.0 (2023-11-01) 143 | 144 | * Introduce a dependency on ember-string to improve out of the box 145 | compatibiliy with Ember 4.12 146 | * Refactor to adopt newer versions of dev dependencies. 147 | 148 | 149 | ## v2.0.0 (2021-07-04) 150 | 151 | 152 | ## v2.0.0-beta.5 (2021-07-04) 153 | 154 | #### :bug: Bug Fix 155 | * [#118](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/118) Avoid the Ember global deprecation ([@mixonic](https://github.com/mixonic)) 156 | 157 | #### Committers: 1 158 | - Matthew Beale ([@mixonic](https://github.com/mixonic)) 159 | 160 | 161 | ## v2.0.0-beta.4 (2021-06-04) 162 | 163 | #### :boom: Breaking Change 164 | * [#116](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/116) [BREAKING] Drop Node 10 ([@mixonic](https://github.com/mixonic)) 165 | 166 | #### :bug: Bug Fix 167 | * [#123](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/123) [BUGFIX] Check the incremented count for limit ([@mixonic](https://github.com/mixonic)) 168 | 169 | #### :house: Internal 170 | * [#125](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/125) Drop ember.component.reopen log ([@mixonic](https://github.com/mixonic)) 171 | * [#124](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/124) Clean up test config for clarity ([@mixonic](https://github.com/mixonic)) 172 | * [#121](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/121) Remove deprecation-without-for and deprecation-without-since warnings in test ([@SergeAstapov](https://github.com/SergeAstapov)) 173 | * [#120](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/120) Update main.js code style ([@mixonic](https://github.com/mixonic)) 174 | * [#119](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/119) Bump deps to remove some deprecated use of Ember global ([@mixonic](https://github.com/mixonic)) 175 | 176 | #### Committers: 2 177 | - Matthew Beale ([@mixonic](https://github.com/mixonic)) 178 | - Sergey Astapov ([@SergeAstapov](https://github.com/SergeAstapov)) 179 | 180 | 181 | ## v2.0.0-beta.3 (2021-05-27) 182 | 183 | #### :boom: Breaking Change 184 | * Upgrade verious dependencies across major versions 185 | 186 | #### :bug: Bug Fix 187 | * [#111](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/111) Address a deprecated import path for deprecate in tests 188 | * [#114](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/114) Drop debug handler polyfill, fixes [#113](https://github.com/mixonic/ember-cli-deprecation-workflow/issues/113). 189 | 190 | #### :rocket: Enhancement 191 | * [#93](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/93) Limit logging when a high-repetition deprecation is firing. 192 | 193 | ## v2.0.0-beta.2 (2021-02-27) 194 | 195 | #### :boom: Breaking Change 196 | * [#92](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/92) Modernize ([@wagenet](https://github.com/wagenet)) 197 | 198 | #### :house: Internal 199 | * [#98](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/98) Add release automation ([@rwjblue](https://github.com/rwjblue)) 200 | * [#97](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/97) Migrate to GH Actions. ([@rwjblue](https://github.com/rwjblue)) 201 | 202 | #### Committers: 3 203 | - Igor Terzic ([@igorT](https://github.com/igorT)) 204 | - Peter Wagenet ([@wagenet](https://github.com/wagenet)) 205 | - Robert Jackson ([@rwjblue](https://github.com/rwjblue)) 206 | 207 | 208 | ## v1.0.1 (2018-11-05) 209 | 210 | #### :bug: Bug Fix 211 | * [#62](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/62) Avoid error when running on nested addons (`this.app` is not always present) ([@SparshithNR](https://github.com/SparshithNR)) 212 | 213 | #### Committers: 1 214 | - SparshithNRai ([@SparshithNR](https://github.com/SparshithNR)) 215 | 216 | ## v1.0.0 (2018-09-26) 217 | 218 | #### :boom: Breaking Change 219 | * [#57](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/57) Update to ember-cli 2.18 ([@Gaurav0](https://github.com/Gaurav0)) 220 | * Drop support for Node 4 and lower. 221 | 222 | #### :rocket: Enhancement 223 | * [#55](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/55) Allow custom addon config directory ([@atsao](https://github.com/atsao)) 224 | * [#58](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/58) Update to ember-cli@3.4 blueprint. ([@rwjblue](https://github.com/rwjblue)) 225 | * [#57](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/57) Update to ember-cli 2.18 ([@Gaurav0](https://github.com/Gaurav0)) 226 | 227 | #### :bug: Bug Fix 228 | * [#59](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/59) Update broccoli node to be broccoli-plugin based. ([@rwjblue](https://github.com/rwjblue)) 229 | 230 | #### :house: Internal 231 | * [#60](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/60) Add Ember 1.13, 2.4, 2.8, and 2.12 back into config/ember-try.js. ([@rwjblue](https://github.com/rwjblue)) 232 | 233 | #### Committers: 3 234 | - Andrew Tsao ([@atsao](https://github.com/atsao)) 235 | - Gaurav Munjal ([@Gaurav0](https://github.com/Gaurav0)) 236 | - Robert Jackson ([@rwjblue](https://github.com/rwjblue)) 237 | 238 | 239 | ## v0.2.5 (2018-08-10) 240 | 241 | #### :bug: Bug Fix 242 | * [#54](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/54) Switch from Ember.Logger to console ([@wagenet](https://github.com/wagenet)) 243 | 244 | #### :memo: Documentation 245 | * [#42](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/42) Update README.md ([@tabeth](https://github.com/tabeth)) 246 | * [#48](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/48) document production builds and catch-all ([@IRun26Point2](https://github.com/IRun26Point2)) 247 | 248 | #### Committers: 3 249 | - Peter Wagenet ([@wagenet](https://github.com/wagenet)) 250 | - Tabeth Nkangoh ([@tabeth](https://github.com/tabeth)) 251 | - [@IRun26Point2](https://github.com/IRun26Point2) 252 | 253 | 254 | ## v0.2.4 (2017-10-18) 255 | 256 | #### :rocket: Enhancement 257 | * [#46](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/46) Convert "ember-cli-babel" to dev dependency ([@Turbo87](https://github.com/Turbo87)) 258 | 259 | #### :memo: Documentation 260 | * [#41](https://github.com/mixonic/ember-cli-deprecation-workflow/pull/41) Update README.md ([@tabeth](https://github.com/tabeth)) 261 | 262 | #### Committers: 2 263 | - Tabeth Nkangoh ([@tabeth](https://github.com/tabeth)) 264 | - Tobias Bieniek ([@Turbo87](https://github.com/Turbo87)) 265 | --------------------------------------------------------------------------------