├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ ├── ci.yml │ ├── plan-release.yml │ └── publish.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.js ├── .release-plan.json ├── .vscode └── settings.json ├── CHANGELOG.md ├── README.md ├── RELEASE.md ├── bin ├── cli.js └── telemetry.js ├── jest.config.js ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── test ├── config │ ├── config.test.ts │ └── fixtures │ │ ├── cjs │ │ └── .codemods.cjs │ │ ├── js │ │ └── .codemods.js │ │ ├── json │ │ └── .codemods.json │ │ ├── package │ │ └── package.json │ │ ├── yaml │ │ └── .codemods.yaml │ │ └── yml │ │ └── .codemods.yml ├── fixtures │ ├── input │ │ ├── .editorconfig │ │ ├── .ember-cli │ │ ├── .eslintignore │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── .prettierignore │ │ ├── .prettierrc.js │ │ ├── .template-lintrc.js │ │ ├── .travis.yml │ │ ├── .watchmanconfig │ │ ├── README.md │ │ ├── app │ │ │ ├── app.js │ │ │ ├── components │ │ │ │ └── test-component.js │ │ │ ├── controllers │ │ │ │ └── .gitkeep │ │ │ ├── helpers │ │ │ │ └── .gitkeep │ │ │ ├── index.html │ │ │ ├── models │ │ │ │ └── .gitkeep │ │ │ ├── router.js │ │ │ ├── routes │ │ │ │ └── .gitkeep │ │ │ ├── styles │ │ │ │ └── app.css │ │ │ ├── templates │ │ │ │ ├── application.hbs │ │ │ │ └── components │ │ │ │ │ └── test-component.hbs │ │ │ └── utils │ │ │ │ └── module-with-error.js │ │ ├── config │ │ │ ├── ember-cli-update.json │ │ │ ├── environment.js │ │ │ ├── optional-features.json │ │ │ └── targets.js │ │ ├── ember-cli-build.js │ │ ├── lib │ │ │ └── special-sauce │ │ │ │ ├── addon │ │ │ │ ├── components │ │ │ │ │ └── fire-sauce.js │ │ │ │ └── templates │ │ │ │ │ └── components │ │ │ │ │ └── fire-sauce.hbs │ │ │ │ ├── index.js │ │ │ │ └── package.json │ │ ├── package.json │ │ ├── public │ │ │ └── robots.txt │ │ ├── testem.js │ │ ├── tests │ │ │ ├── helpers │ │ │ │ └── .gitkeep │ │ │ ├── index.html │ │ │ ├── integration │ │ │ │ ├── .gitkeep │ │ │ │ └── components │ │ │ │ │ ├── fire-sauce-test.js │ │ │ │ │ └── test-component-test.js │ │ │ ├── test-helper.js │ │ │ └── unit │ │ │ │ └── .gitkeep │ │ ├── vendor │ │ │ └── .gitkeep │ │ └── yarn.lock │ └── output │ │ ├── .editorconfig │ │ ├── .ember-cli │ │ ├── .eslintignore │ │ ├── .eslintrc.js │ │ ├── .gitignore │ │ ├── .template-lintrc.js │ │ ├── .travis.yml │ │ ├── .watchmanconfig │ │ ├── README.md │ │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ └── test-component.js │ │ ├── controllers │ │ │ └── .gitkeep │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.css │ │ ├── templates │ │ │ ├── application.hbs │ │ │ └── components │ │ │ │ └── test-component.hbs │ │ └── utils │ │ │ └── module-with-error.js │ │ ├── config │ │ ├── environment.js │ │ ├── optional-features.json │ │ └── targets.js │ │ ├── ember-cli-build.js │ │ ├── lib │ │ └── special-sauce │ │ │ ├── addon │ │ │ ├── components │ │ │ │ └── fire-sauce.js │ │ │ └── templates │ │ │ │ └── components │ │ │ │ └── fire-sauce.hbs │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── package.json │ │ ├── public │ │ └── robots.txt │ │ ├── testem.js │ │ ├── tests │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── integration │ │ │ ├── .gitkeep │ │ │ └── components │ │ │ │ ├── fire-sauce-test.js │ │ │ │ └── test-component-test.js │ │ ├── test-helper.js │ │ └── unit │ │ │ └── .gitkeep │ │ ├── vendor │ │ └── .gitkeep │ │ └── yarn.lock ├── helpers │ └── expect-multiline.ts ├── options.test.ts └── run-test.js ├── transforms ├── .gitkeep ├── ember-object │ ├── README.md │ ├── __testfixtures__ │ │ ├── -mock-telemetry.json │ │ ├── action-invalid-1.input.js │ │ ├── action-invalid-1.output.js │ │ ├── action-invalid-2.input.js │ │ ├── action-invalid-2.output.js │ │ ├── action-invalid-3.input.js │ │ ├── action-invalid-3.output.js │ │ ├── action-invalid-4.input.js │ │ ├── action-invalid-4.output.js │ │ ├── action-invalid-5.input.js │ │ ├── action-invalid-5.output.js │ │ ├── async-function-property.input.js │ │ ├── async-function-property.output.js │ │ ├── async-method.input.js │ │ ├── async-method.output.js │ │ ├── basic-computed.input.js │ │ ├── basic-computed.output.js │ │ ├── basic.input.js │ │ ├── basic.output.js │ │ ├── chained-class-definition.input.js │ │ ├── chained-class-definition.output.js │ │ ├── class-fields.input.js │ │ ├── class-fields.output.js │ │ ├── class-reopen.input.js │ │ ├── class-reopen.output.js │ │ ├── decorators-invalid-1.input.js │ │ ├── decorators-invalid-1.output.js │ │ ├── decorators-invalid-2.input.js │ │ ├── decorators-invalid-2.options.json │ │ ├── decorators-invalid-2.output.js │ │ ├── decorators-invalid-3.input.js │ │ ├── decorators-invalid-3.output.js │ │ ├── decorators-invalid-4.input.js │ │ ├── decorators-invalid-4.options.json │ │ ├── decorators-invalid-4.output.js │ │ ├── decorators-invalid-5.input.js │ │ ├── decorators-invalid-5.output.js │ │ ├── decorators-invalid-6.input.js │ │ ├── decorators-invalid-6.output.js │ │ ├── decorators-invalid-7.input.js │ │ ├── decorators-invalid-7.output.js │ │ ├── decorators-invalid-8.input.js │ │ ├── decorators-invalid-8.output.js │ │ ├── decorators.input.js │ │ ├── decorators.output.js │ │ ├── default-export.input.js │ │ ├── default-export.output.js │ │ ├── double-quotes.input.js │ │ ├── double-quotes.options.json │ │ ├── double-quotes.output.js │ │ ├── ember-concurrency.input.js │ │ ├── ember-concurrency.output.js │ │ ├── frozen.input.js │ │ ├── frozen.output.js │ │ ├── generator-method.input.js │ │ ├── generator-method.output.js │ │ ├── generator-property.input.js │ │ ├── generator-property.output.js │ │ ├── import.input.js │ │ ├── import.output.js │ │ ├── inject-basic.input.js │ │ ├── inject-basic.output.js │ │ ├── injecting-service.input.js │ │ ├── injecting-service.output.js │ │ ├── logical-expression.input.js │ │ ├── logical-expression.output.js │ │ ├── mixin.input.js │ │ ├── mixin.output.js │ │ ├── object-literal-with-action-hash-and-decorator.input.js │ │ ├── object-literal-with-action-hash-and-decorator.output.js │ │ ├── object-literal-with-decorators-invalid-1.input.js │ │ ├── object-literal-with-decorators-invalid-1.options.json │ │ ├── object-literal-with-decorators-invalid-1.output.js │ │ ├── object-literal-with-decorators-invalid-2.input.js │ │ ├── object-literal-with-decorators-invalid-2.options.json │ │ ├── object-literal-with-decorators-invalid-2.output.js │ │ ├── object-literal-with-decorators-invalid-3.input.js │ │ ├── object-literal-with-decorators-invalid-3.options.json │ │ ├── object-literal-with-decorators-invalid-3.output.js │ │ ├── object-literal-with-decorators-invalid-4.input.js │ │ ├── object-literal-with-decorators-invalid-4.options.json │ │ ├── object-literal-with-decorators-invalid-4.output.js │ │ ├── object-literal-with-decorators.input.js │ │ ├── object-literal-with-decorators.output.js │ │ ├── options │ │ │ ├── class-fields-disabled.input.js │ │ │ ├── class-fields-disabled.options.json │ │ │ ├── class-fields-disabled.output.js │ │ │ ├── classic-decorator-disabled.input.js │ │ │ ├── classic-decorator-disabled.options.json │ │ │ ├── classic-decorator-disabled.output.js │ │ │ ├── decorators-disabled-1.input.js │ │ │ ├── decorators-disabled-1.options.json │ │ │ ├── decorators-disabled-1.output.js │ │ │ ├── decorators-disabled-10.input.js │ │ │ ├── decorators-disabled-10.options.json │ │ │ ├── decorators-disabled-10.output.js │ │ │ ├── decorators-disabled-11.input.js │ │ │ ├── decorators-disabled-11.options.json │ │ │ ├── decorators-disabled-11.output.js │ │ │ ├── decorators-disabled-12.input.js │ │ │ ├── decorators-disabled-12.options.json │ │ │ ├── decorators-disabled-12.output.js │ │ │ ├── decorators-disabled-13.input.js │ │ │ ├── decorators-disabled-13.options.json │ │ │ ├── decorators-disabled-13.output.js │ │ │ ├── decorators-disabled-2.input.js │ │ │ ├── decorators-disabled-2.options.json │ │ │ ├── decorators-disabled-2.output.js │ │ │ ├── decorators-disabled-3.input.js │ │ │ ├── decorators-disabled-3.options.json │ │ │ ├── decorators-disabled-3.output.js │ │ │ ├── decorators-disabled-4.input.js │ │ │ ├── decorators-disabled-4.options.json │ │ │ ├── decorators-disabled-4.output.js │ │ │ ├── decorators-disabled-5.input.js │ │ │ ├── decorators-disabled-5.options.json │ │ │ ├── decorators-disabled-5.output.js │ │ │ ├── decorators-disabled-6.input.js │ │ │ ├── decorators-disabled-6.options.json │ │ │ ├── decorators-disabled-6.output.js │ │ │ ├── decorators-disabled-7.input.js │ │ │ ├── decorators-disabled-7.options.json │ │ │ ├── decorators-disabled-7.output.js │ │ │ ├── decorators-disabled-8.input.js │ │ │ ├── decorators-disabled-8.options.json │ │ │ ├── decorators-disabled-8.output.js │ │ │ ├── decorators-disabled-9.input.js │ │ │ ├── decorators-disabled-9.options.json │ │ │ ├── decorators-disabled-9.output.js │ │ │ ├── ignore-leaking-state.input.js │ │ │ ├── ignore-leaking-state.options.json │ │ │ ├── ignore-leaking-state.output.js │ │ │ ├── object-literal-decorators.input.js │ │ │ ├── object-literal-decorators.options.json │ │ │ ├── object-literal-decorators.output.js │ │ │ ├── query-params-disallowed.input.js │ │ │ ├── query-params-disallowed.options.json │ │ │ ├── query-params-disallowed.output.js │ │ │ ├── string-boolean.input.js │ │ │ ├── string-boolean.options.json │ │ │ └── string-boolean.output.js │ │ ├── runtime.input.js │ │ ├── runtime.options.json │ │ ├── runtime.output.js │ │ ├── service-basic.input.js │ │ ├── service-basic.output.js │ │ └── types │ │ │ ├── components │ │ │ ├── basic.input.js │ │ │ └── basic.output.js │ │ │ ├── controllers │ │ │ ├── basic.input.js │ │ │ └── basic.output.js │ │ │ ├── helpers │ │ │ ├── basic.input.js │ │ │ └── basic.output.js │ │ │ ├── invalid │ │ │ ├── basic.input.js │ │ │ ├── basic.options.json │ │ │ └── basic.output.js │ │ │ ├── pods │ │ │ └── foo │ │ │ │ ├── controller.input.js │ │ │ │ └── controller.output.js │ │ │ ├── routes │ │ │ ├── basic.input.js │ │ │ └── basic.output.js │ │ │ └── services │ │ │ ├── basic.input.js │ │ │ ├── basic.options.json │ │ │ ├── basic.output.js │ │ │ ├── some-service.input.js │ │ │ └── some-service.output.js │ ├── index.ts │ └── test.ts └── helpers │ ├── ast.ts │ ├── config.ts │ ├── decorator-helper.ts │ ├── decorator-info.ts │ ├── eo-extend-expression.ts │ ├── eo-prop │ ├── index.ts │ └── private │ │ ├── abstract.ts │ │ ├── actions │ │ ├── index.ts │ │ ├── method.ts │ │ └── property.ts │ │ ├── class-decorator.ts │ │ ├── computed │ │ ├── abstract.ts │ │ ├── function-expression-getter.ts │ │ ├── function-expression-method.ts │ │ ├── index.ts │ │ ├── modifier-helper.ts │ │ ├── object-expression.ts │ │ └── property.ts │ │ ├── function-expression.ts │ │ ├── method.ts │ │ └── simple.ts │ ├── import-helper.ts │ ├── log-helper.ts │ ├── options.ts │ ├── parse-helper.ts │ ├── parse.ts │ ├── runtime-data.ts │ ├── schema-helper.ts │ ├── transform-helper.ts │ ├── transform.ts │ ├── util │ ├── index.ts │ └── types.ts │ └── validation-helper.ts ├── tsconfig.json ├── tsconfig.lint.json └── types ├── codemod-cli.d.ts ├── ember-codemods-telemetry-helpers.d.ts └── jscodeshift └── dist └── testUtils.d.ts /.eslintignore: -------------------------------------------------------------------------------- 1 | **/__testfixtures__/**/*.js 2 | **/fixtures/** 3 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [16.x, 18.x, 20.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: pnpm/action-setup@v2 20 | name: Install pnpm 21 | with: 22 | version: 8 23 | - uses: actions/setup-node@v4 24 | with: 25 | node-version: ${{ matrix.node-version }} 26 | cache: pnpm 27 | - run: pnpm install --frozen-lockfile 28 | - run: pnpm lint 29 | - run: pnpm test 30 | -------------------------------------------------------------------------------- /.github/workflows/plan-release.yml: -------------------------------------------------------------------------------- 1 | name: Release Plan Review 2 | on: 3 | push: 4 | branches: 5 | - main 6 | - master 7 | pull_request: 8 | types: 9 | - labeled 10 | 11 | concurrency: 12 | group: plan-release # only the latest one of these should ever be running 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | check-plan: 17 | name: 'Check Release Plan' 18 | runs-on: ubuntu-latest 19 | outputs: 20 | command: ${{ steps.check-release.outputs.command }} 21 | 22 | steps: 23 | - uses: actions/checkout@v4 24 | with: 25 | fetch-depth: 0 26 | ref: 'main' 27 | # This will only cause the `check-plan` job to have a "command" of `release` 28 | # when the .release-plan.json file was changed on the last commit. 29 | - id: check-release 30 | run: if git diff --name-only HEAD HEAD~1 | grep -w -q ".release-plan.json"; then echo "command=release"; fi >> $GITHUB_OUTPUT 31 | 32 | prepare_release_notes: 33 | name: Prepare Release Notes 34 | runs-on: ubuntu-latest 35 | timeout-minutes: 5 36 | needs: check-plan 37 | outputs: 38 | explanation: ${{ steps.explanation.outputs.text }} 39 | # only run on push event if plan wasn't updated (don't create a release plan when we're releasing) 40 | # only run on labeled event if the PR has already been merged 41 | if: (github.event_name == 'push' && needs.check-plan.outputs.command != 'release') || (github.event_name == 'pull_request' && github.event.pull_request.merged == true) 42 | 43 | steps: 44 | - uses: actions/checkout@v4 45 | # We need to download lots of history so that 46 | # lerna-changelog can discover what's changed since the last release 47 | with: 48 | fetch-depth: 0 49 | - uses: actions/setup-node@v4 50 | with: 51 | node-version: 18 52 | 53 | - uses: pnpm/action-setup@v2 54 | with: 55 | version: 8 56 | - run: pnpm install --frozen-lockfile 57 | 58 | - name: 'Generate Explanation and Prep Changelogs' 59 | id: explanation 60 | run: | 61 | set -x 62 | 63 | pnpm release-plan prepare 64 | 65 | echo 'text<> $GITHUB_OUTPUT 66 | jq .description .release-plan.json -r >> $GITHUB_OUTPUT 67 | echo 'EOF' >> $GITHUB_OUTPUT 68 | env: 69 | GITHUB_AUTH: ${{ secrets.GITHUB_TOKEN }} 70 | 71 | - uses: peter-evans/create-pull-request@v5 72 | with: 73 | commit-message: "Prepare Release using 'release-plan'" 74 | author: 'github-actions[bot] ' 75 | labels: 'internal' 76 | branch: release-preview 77 | title: Prepare Release 78 | body: | 79 | 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 👍 80 | 81 | ----------------------------------------- 82 | 83 | ${{ steps.explanation.outputs.text }} 84 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | # For every push to the master branch, this checks if the release-plan was 2 | # updated and if it was it will publish stable npm packages based on the 3 | # release plan 4 | 5 | name: Publish Stable 6 | 7 | on: 8 | workflow_dispatch: 9 | push: 10 | branches: 11 | - main 12 | - master 13 | 14 | concurrency: 15 | group: publish-${{ github.head_ref || github.ref }} 16 | cancel-in-progress: true 17 | 18 | jobs: 19 | check-plan: 20 | name: 'Check Release Plan' 21 | runs-on: ubuntu-latest 22 | outputs: 23 | command: ${{ steps.check-release.outputs.command }} 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | with: 28 | fetch-depth: 0 29 | ref: 'main' 30 | # This will only cause the `check-plan` job to have a result of `success` 31 | # when the .release-plan.json file was changed on the last commit. This 32 | # plus the fact that this action only runs on main will be enough of a guard 33 | - id: check-release 34 | run: if git diff --name-only HEAD HEAD~1 | grep -w -q ".release-plan.json"; then echo "command=release"; fi >> $GITHUB_OUTPUT 35 | 36 | publish: 37 | name: 'NPM Publish' 38 | runs-on: ubuntu-latest 39 | needs: check-plan 40 | if: needs.check-plan.outputs.command == 'release' 41 | 42 | steps: 43 | - uses: actions/checkout@v4 44 | - uses: actions/setup-node@v4 45 | with: 46 | node-version: 18 47 | # This creates an .npmrc that reads the NODE_AUTH_TOKEN environment variable 48 | registry-url: 'https://registry.npmjs.org' 49 | 50 | - uses: pnpm/action-setup@v2 51 | with: 52 | version: 8 53 | - run: pnpm install --frozen-lockfile 54 | - name: npm publish 55 | run: pnpm release-plan publish 56 | 57 | env: 58 | GITHUB_AUTH: ${{ secrets.GITHUB_TOKEN }} 59 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .eslintcache 4 | transforms/**/*.js 5 | !transforms/ember-object/__testfixtures__/**/*.js 6 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .eslintcache 4 | **/__testfixtures__/** 5 | **/fixtures/** 6 | CHANGELOG.md 7 | README.md 8 | pnpm-lock.yaml -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | singleQuote: true, 5 | printWidth: 100, 6 | overrides: [ 7 | { 8 | files: '*.ts', 9 | options: { 10 | printWidth: 80, 11 | }, 12 | }, 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /.release-plan.json: -------------------------------------------------------------------------------- 1 | { 2 | "solution": { 3 | "ember-native-class-codemod": { 4 | "impact": "patch", 5 | "oldVersion": "4.1.0", 6 | "newVersion": "4.1.1", 7 | "constraints": [ 8 | { 9 | "impact": "patch", 10 | "reason": "Appears in changelog section :bug: Bug Fix" 11 | }, 12 | { 13 | "impact": "patch", 14 | "reason": "Appears in changelog section :house: Internal" 15 | } 16 | ], 17 | "pkgJSONPath": "./package.json" 18 | } 19 | }, 20 | "description": "## Release (2024-02-26)\n\nember-native-class-codemod 4.1.1 (patch)\n\n#### :bug: Bug Fix\n* `ember-native-class-codemod`\n * [#552](https://github.com/ember-codemods/ember-native-class-codemod/pull/552) Fix off-by-one-error when interpreting CLI args ([@gitKrystan](https://github.com/gitKrystan))\n\n#### :house: Internal\n* `ember-native-class-codemod`\n * [#551](https://github.com/ember-codemods/ember-native-class-codemod/pull/551) Don't publish TS files ([@gitKrystan](https://github.com/gitKrystan))\n\n#### Committers: 1\n- Krystan HuffMenne ([@gitKrystan](https://github.com/gitKrystan))\n" 21 | } 22 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.defaultFormatter": "esbenp.prettier-vscode", 4 | "editor.codeActionsOnSave": { 5 | "source.fixAll": "explicit", 6 | "source.organizeImports": "explicit" 7 | }, 8 | "typescript.preferences.importModuleSpecifierEnding": "index", 9 | "typescript.suggest.autoImports": true 10 | } 11 | -------------------------------------------------------------------------------- /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 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-codemods/ember-native-class-codemod/pulls?q=is%3Apr+is%3Aopen+%22Prepare+Release%22+in%3Atitle) PR 28 | -------------------------------------------------------------------------------- /bin/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | const { gatherTelemetryForUrl, analyzeEmberObject } = require('ember-codemods-telemetry-helpers'); 5 | 6 | (async () => { 7 | // `NO_TELEMETRY=true npx ember-native-class-codemod path` 8 | // => ['node', 'ember-native-class-codemod', 'path'] 9 | let args = process.argv.slice(2); 10 | if (process.env['NO_TELEMETRY'] !== 'true') { 11 | await gatherTelemetryForUrl(process.argv[2], analyzeEmberObject); 12 | // `npx ember-native-class-codemod http://localhost path` 13 | // => ['node', 'ember-native-class-codemod', 'http://localhost', 'path'] 14 | args = process.argv.slice(3); 15 | } 16 | 17 | require('codemod-cli').runTransform(__dirname, 'ember-object', args); 18 | })(); 19 | -------------------------------------------------------------------------------- /bin/telemetry.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | const { gatherTelemetry, analyzeEmberObject } = require('ember-codemods-telemetry-helpers'); 5 | 6 | gatherTelemetry(process.argv[2], analyzeEmberObject); 7 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line jsdoc/valid-types 2 | /** @type {import('ts-jest').JestConfigWithTsJest} */ 3 | module.exports = { 4 | preset: 'ts-jest', 5 | testEnvironment: 'node', 6 | }; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-native-class-codemod", 3 | "version": "4.1.1", 4 | "description": "Codemods for transforming ember app code to native class syntax with decorators.", 5 | "keywords": [ 6 | "codemod-cli" 7 | ], 8 | "homepage": "https://github.com/ember-codemods/ember-native-class-codemod#readme", 9 | "bugs": { 10 | "url": "https://github.com/ember-codemods/ember-native-class-codemod/issues" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/ember-codemods/ember-native-class-codemod.git" 15 | }, 16 | "license": "MIT", 17 | "author": "", 18 | "main": ".eslintrc.js", 19 | "bin": { 20 | "ember-native-class-codemod": "./bin/cli.js" 21 | }, 22 | "directories": { 23 | "lib": "lib", 24 | "test": "test" 25 | }, 26 | "files": [ 27 | "/bin", 28 | "/lib", 29 | "/transforms/helpers/**/*.js", 30 | "/transforms/ember-object/index.js" 31 | ], 32 | "scripts": { 33 | "build": "tsc", 34 | "clean": "tsc --build --clean && git checkout test/fixtures", 35 | "lint": "concurrently \"npm:lint:*(!fix)\" --names \"lint:\"", 36 | "lint:fix": "concurrently \"npm:lint:*:fix\" --names \"fix:\"", 37 | "lint:js": "eslint . --cache", 38 | "lint:js:fix": "eslint . --fix", 39 | "lint:prettier": "prettier --check .", 40 | "lint:prettier:fix": "prettier --write .", 41 | "lint:ts": "tsc --noEmit", 42 | "prepublishOnly": "pnpm build", 43 | "postpublish": "pnpm clean", 44 | "test": "pnpm build && codemod-cli test && node ./test/run-test.js && pnpm clean", 45 | "update-docs": "codemod-cli update-docs" 46 | }, 47 | "dependencies": { 48 | "@babel/parser": "^7.21.4", 49 | "camelcase": "^6.3.0", 50 | "codemod-cli": "^3.2.0", 51 | "cosmiconfig": "^7.0.0", 52 | "deepmerge-ts": "^4.3.0", 53 | "ember-codemods-telemetry-helpers": "^3.0.0", 54 | "jscodeshift": "^0.11.0", 55 | "minimatch": "^7.4.6", 56 | "winston": "^3.8.2", 57 | "zod": "^3.21.4" 58 | }, 59 | "devDependencies": { 60 | "@babel/core": "^7.21.4", 61 | "@babel/eslint-parser": "^7.21.3", 62 | "@babel/preset-env": "^7.21.4", 63 | "@jest/globals": "^29.5.0", 64 | "@tsconfig/node12": "^1.0.11", 65 | "@types/babel__core": "^7.20.5", 66 | "@types/glob": "^8.1.0", 67 | "@types/jscodeshift": "^0.11.6", 68 | "@typescript-eslint/eslint-plugin": "^5.59.1", 69 | "@typescript-eslint/parser": "^5.59.1", 70 | "ast-types": "^0.14.2", 71 | "concurrently": "^8.0.1", 72 | "eslint": "^8.39.0", 73 | "eslint-config-prettier": "^8.8.0", 74 | "eslint-plugin-eslint-comments": "^3.2.0", 75 | "eslint-plugin-jsdoc": "43.0.5", 76 | "eslint-plugin-node": "^11.1.0", 77 | "eslint-plugin-unicorn": "^46.0.0", 78 | "execa": "^5.1.1", 79 | "glob": "^8.1.0", 80 | "jest": "^29.5.0", 81 | "prettier": "^2.8.8", 82 | "release-plan": "^0.5.0", 83 | "ts-jest": "^29.1.0", 84 | "typescript": "^5.0.4" 85 | }, 86 | "engines": { 87 | "node": "16.* || 18.* || >= 20.*" 88 | }, 89 | "publishConfig": { 90 | "registry": "https://registry.npmjs.org" 91 | }, 92 | "volta": { 93 | "node": "16.20.2", 94 | "yarn": "1.22.21" 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | # TODO delete this file after https://github.com/embroider-build/release-plan/issues/36 is fixed 2 | packages: 3 | - '.' 4 | -------------------------------------------------------------------------------- /test/config/config.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from '@jest/globals'; 2 | import path from 'path'; 3 | import getConfig, { mergeConfig } from '../../transforms/helpers/config'; 4 | import { DEFAULT_OPTIONS } from '../../transforms/helpers/options'; 5 | 6 | describe('config', () => { 7 | describe('getConfig', () => { 8 | test('it has default options', () => { 9 | const config = getConfig(); 10 | expect(config).toStrictEqual(DEFAULT_OPTIONS); 11 | }); 12 | 13 | test.each(['json', 'js', 'cjs', 'yml', 'yaml', 'package'])( 14 | 'should read %s config', 15 | (fixture) => { 16 | const config = getConfig(pathFor(fixture)); 17 | expect(config).toStrictEqual( 18 | mergeConfig(DEFAULT_OPTIONS, { 19 | classFields: false, 20 | quote: 'double', 21 | decorators: { 22 | inObjectLiterals: [`${fixture}DecOne`, `${fixture}DecTwo`], 23 | }, 24 | }) 25 | ); 26 | } 27 | ); 28 | }); 29 | 30 | describe('mergeConfig', () => { 31 | test('it allows overwriting the ignoreLeakingState config', () => { 32 | expect( 33 | mergeConfig(DEFAULT_OPTIONS, { 34 | ignoreLeakingState: ['queryParams', 'ignoreMyLeakyState'], 35 | }) 36 | ).toStrictEqual({ 37 | ...DEFAULT_OPTIONS, 38 | ignoreLeakingState: ['queryParams', 'ignoreMyLeakyState'], 39 | }); 40 | }); 41 | 42 | test('it deep merges the decorators config', () => { 43 | expect( 44 | mergeConfig(DEFAULT_OPTIONS, { 45 | decorators: { 46 | inObjectLiterals: ['computer'], 47 | }, 48 | }) 49 | ).toStrictEqual({ 50 | ...DEFAULT_OPTIONS, 51 | decorators: { 52 | inObjectLiterals: ['computer'], 53 | }, 54 | }); 55 | }); 56 | }); 57 | }); 58 | 59 | function pathFor(extension: string): string { 60 | // eslint-disable-next-line unicorn/prefer-module 61 | return path.resolve(__dirname, `./fixtures/${extension}`); 62 | } 63 | -------------------------------------------------------------------------------- /test/config/fixtures/cjs/.codemods.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | classFields: false, 3 | quote: 'double', 4 | decorators: { 5 | inObjectLiterals: ['cjsDecOne', 'cjsDecTwo'] 6 | } 7 | }; -------------------------------------------------------------------------------- /test/config/fixtures/js/.codemods.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | classFields: false, 3 | quote: 'double', 4 | decorators: { 5 | inObjectLiterals: ['jsDecOne', 'jsDecTwo'] 6 | } 7 | }; -------------------------------------------------------------------------------- /test/config/fixtures/json/.codemods.json: -------------------------------------------------------------------------------- 1 | { 2 | "classFields": false, 3 | "quote": "double", 4 | "decorators": { 5 | "inObjectLiterals": ["jsonDecOne", "jsonDecTwo"] 6 | } 7 | } -------------------------------------------------------------------------------- /test/config/fixtures/package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "codemods": { 3 | "classFields": false, 4 | "quote": "double", 5 | "decorators": { 6 | "inObjectLiterals": ["packageDecOne", "packageDecTwo"] 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /test/config/fixtures/yaml/.codemods.yaml: -------------------------------------------------------------------------------- 1 | classFields: false 2 | quote: 'double' 3 | decorators: 4 | inObjectLiterals: 5 | - yamlDecOne 6 | - yamlDecTwo 7 | -------------------------------------------------------------------------------- /test/config/fixtures/yml/.codemods.yml: -------------------------------------------------------------------------------- 1 | classFields: false 2 | quote: 'double' 3 | decorators: 4 | inObjectLiterals: 5 | - ymlDecOne 6 | - ymlDecTwo 7 | -------------------------------------------------------------------------------- /test/fixtures/input/.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 | -------------------------------------------------------------------------------- /test/fixtures/input/.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/input/.eslintignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | .*/ 17 | .eslintcache 18 | 19 | # ember-try 20 | /.node_modules.ember-try/ 21 | /bower.json.ember-try 22 | /package.json.ember-try 23 | -------------------------------------------------------------------------------- /test/fixtures/input/.eslintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | ecmaVersion: 2018, 8 | sourceType: 'module', 9 | ecmaFeatures: { 10 | legacyDecorators: true, 11 | }, 12 | }, 13 | plugins: ['ember'], 14 | extends: [ 15 | 'eslint:recommended', 16 | 'plugin:ember/recommended', 17 | 'plugin:prettier/recommended', 18 | ], 19 | env: { 20 | browser: true, 21 | }, 22 | rules: {}, 23 | overrides: [ 24 | // node files 25 | { 26 | files: [ 27 | './.eslintrc.js', 28 | './.prettierrc.js', 29 | './.template-lintrc.js', 30 | './ember-cli-build.js', 31 | './testem.js', 32 | './blueprints/*/index.js', 33 | './config/**/*.js', 34 | './lib/*/index.js', 35 | './server/**/*.js', 36 | ], 37 | parserOptions: { 38 | sourceType: 'script', 39 | }, 40 | env: { 41 | browser: false, 42 | node: true, 43 | }, 44 | plugins: ['node'], 45 | extends: ['plugin:node/recommended'], 46 | rules: { 47 | // this can be removed once the following is fixed 48 | // https://github.com/mysticatea/eslint-plugin-node/issues/77 49 | 'node/no-unpublished-require': 'off', 50 | }, 51 | }, 52 | { 53 | // Test files: 54 | files: ['tests/**/*-test.{js,ts}'], 55 | extends: ['plugin:qunit/recommended'], 56 | }, 57 | ], 58 | }; 59 | -------------------------------------------------------------------------------- /test/fixtures/input/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist/ 5 | /tmp/ 6 | 7 | # dependencies 8 | /bower_components/ 9 | /node_modules/ 10 | 11 | # misc 12 | /.env* 13 | /.pnp* 14 | /.sass-cache 15 | /.eslintcache 16 | /connect.lock 17 | /coverage/ 18 | /libpeerconnection.log 19 | /npm-debug.log* 20 | /testem.log 21 | /yarn-error.log 22 | 23 | # ember-try 24 | /.node_modules.ember-try/ 25 | /bower.json.ember-try 26 | /package.json.ember-try 27 | -------------------------------------------------------------------------------- /test/fixtures/input/.prettierignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | .eslintcache 17 | 18 | # ember-try 19 | /.node_modules.ember-try/ 20 | /bower.json.ember-try 21 | /package.json.ember-try 22 | -------------------------------------------------------------------------------- /test/fixtures/input/.prettierrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | singleQuote: true, 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/input/.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended', 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/input/.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | - "12" 5 | 6 | dist: xenial 7 | 8 | addons: 9 | chrome: stable 10 | 11 | cache: 12 | yarn: true 13 | 14 | env: 15 | global: 16 | # See https://git.io/vdao3 for details. 17 | - JOBS=1 18 | 19 | branches: 20 | only: 21 | - master 22 | 23 | before_install: 24 | - curl -o- -L https://yarnpkg.com/install.sh | bash 25 | - export PATH=$HOME/.yarn/bin:$PATH 26 | 27 | script: 28 | - yarn test 29 | -------------------------------------------------------------------------------- /test/fixtures/input/.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/input/README.md: -------------------------------------------------------------------------------- 1 | # input 2 | 3 | This README outlines the details of collaborating on this Ember application. 4 | A short introduction of this app could easily go here. 5 | 6 | ## Prerequisites 7 | 8 | You will need the following things properly installed on your computer. 9 | 10 | * [Git](https://git-scm.com/) 11 | * [Node.js](https://nodejs.org/) 12 | * [Yarn](https://yarnpkg.com/) 13 | * [Ember CLI](https://ember-cli.com/) 14 | * [Google Chrome](https://google.com/chrome/) 15 | 16 | ## Installation 17 | 18 | * `git clone ` this repository 19 | * `cd input` 20 | * `yarn install` 21 | 22 | ## Running / Development 23 | 24 | * `ember serve` 25 | * Visit your app at [http://localhost:4200](http://localhost:4200). 26 | * Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests). 27 | 28 | ### Code Generators 29 | 30 | Make use of the many generators for code, try `ember help generate` for more details 31 | 32 | ### Running Tests 33 | 34 | * `ember test` 35 | * `ember test --server` 36 | 37 | ### Linting 38 | 39 | * `yarn lint` 40 | * `yarn lint:fix` 41 | 42 | ### Building 43 | 44 | * `ember build` (development) 45 | * `ember build --environment production` (production) 46 | 47 | ### Deploying 48 | 49 | Specify what it takes to deploy your app. 50 | 51 | ## Further Reading / Useful Links 52 | 53 | * [ember.js](https://emberjs.com/) 54 | * [ember-cli](https://ember-cli.com/) 55 | * Development Browser Extensions 56 | * [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi) 57 | * [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/) 58 | -------------------------------------------------------------------------------- /test/fixtures/input/app/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from 'ember-resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from 'input/config/environment'; 5 | 6 | export default class App extends Application { 7 | modulePrefix = config.modulePrefix; 8 | podModulePrefix = config.podModulePrefix; 9 | Resolver = Resolver; 10 | } 11 | 12 | loadInitializers(App, config.modulePrefix); 13 | -------------------------------------------------------------------------------- /test/fixtures/input/app/components/test-component.js: -------------------------------------------------------------------------------- 1 | import Component from '@ember/component'; 2 | import { computed } from '@ember/object'; 3 | 4 | function fullNameMacro() { 5 | return computed('firstName', 'lastName', function() { 6 | return `${this.firstName} ${this.lastName}`; 7 | }); 8 | } 9 | 10 | export default Component.extend({ 11 | fullName: fullNameMacro(), 12 | }); 13 | -------------------------------------------------------------------------------- /test/fixtures/input/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/input/app/controllers/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/input/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/input/app/helpers/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/input/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Input 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} 16 | 17 | 18 | {{content-for "body"}} 19 | 20 | 21 | 22 | 23 | {{content-for "body-footer"}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /test/fixtures/input/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/input/app/models/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/input/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from './config/environment'; 3 | 4 | const Router = EmberRouter.extend({ 5 | location: config.locationType, 6 | rootURL: config.rootURL, 7 | }); 8 | 9 | Router.map(function () {}); 10 | 11 | export default Router; 12 | -------------------------------------------------------------------------------- /test/fixtures/input/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/input/app/routes/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/input/app/styles/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/input/app/styles/app.css -------------------------------------------------------------------------------- /test/fixtures/input/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | {{page-title "Input"}} 2 | 3 | {{!-- The following component displays Ember's default welcome message. --}} 4 | 5 | {{!-- Feel free to remove this! --}} 6 | 7 | {{outlet}} -------------------------------------------------------------------------------- /test/fixtures/input/app/templates/components/test-component.hbs: -------------------------------------------------------------------------------- 1 | {{yield}} -------------------------------------------------------------------------------- /test/fixtures/input/app/utils/module-with-error.js: -------------------------------------------------------------------------------- 1 | throw new Error('This should not fail the telemetry gathering!'); 2 | -------------------------------------------------------------------------------- /test/fixtures/input/config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "packages": [ 4 | { 5 | "name": "ember-cli", 6 | "version": "3.28.6", 7 | "blueprints": [ 8 | { 9 | "name": "app", 10 | "outputRepo": "https://github.com/ember-cli/ember-new-output", 11 | "codemodsSource": "ember-app-codemods-manifest@1", 12 | "isBaseBlueprint": true, 13 | "options": [ 14 | "--yarn" 15 | ] 16 | } 17 | ] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /test/fixtures/input/config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (environment) { 4 | let ENV = { 5 | modulePrefix: 'input', 6 | environment, 7 | rootURL: '/', 8 | locationType: 'auto', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true 13 | }, 14 | EXTEND_PROTOTYPES: { 15 | // Prevent Ember Data from overriding Date.parse. 16 | Date: false, 17 | }, 18 | }, 19 | 20 | APP: { 21 | // Here you can pass flags/options to your application instance 22 | // when it is created 23 | }, 24 | }; 25 | 26 | if (environment === 'development') { 27 | // ENV.APP.LOG_RESOLVER = true; 28 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 29 | // ENV.APP.LOG_TRANSITIONS = true; 30 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 31 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 32 | } 33 | 34 | if (environment === 'test') { 35 | // Testem prefers this... 36 | ENV.locationType = 'none'; 37 | 38 | // keep test console output quieter 39 | ENV.APP.LOG_ACTIVE_GENERATION = false; 40 | ENV.APP.LOG_VIEW_LOOKUPS = false; 41 | 42 | ENV.APP.rootElement = '#ember-testing'; 43 | ENV.APP.autoboot = false; 44 | } 45 | 46 | if (environment === 'production') { 47 | // here you can enable a production-specific feature 48 | } 49 | 50 | return ENV; 51 | }; 52 | -------------------------------------------------------------------------------- /test/fixtures/input/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "application-template-wrapper": false, 3 | "default-async-observers": true, 4 | "jquery-integration": true, 5 | "template-only-glimmer-components": true 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/input/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const browsers = [ 4 | 'last 1 Chrome versions', 5 | 'last 1 Firefox versions', 6 | 'last 1 Safari versions', 7 | ]; 8 | 9 | // Ember's browser support policy is changing, and IE11 support will end in 10 | // v4.0 onwards. 11 | // 12 | // See https://deprecations.emberjs.com/v3.x#toc_3-0-browser-support-policy 13 | // 14 | // If you need IE11 support on a version of Ember that still offers support 15 | // for it, uncomment the code block below. 16 | // 17 | // const isCI = Boolean(process.env.CI); 18 | // const isProduction = process.env.EMBER_ENV === 'production'; 19 | // 20 | // if (isCI || isProduction) { 21 | // browsers.push('ie 11'); 22 | // } 23 | 24 | module.exports = { 25 | browsers, 26 | }; 27 | -------------------------------------------------------------------------------- /test/fixtures/input/ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EmberApp = require('ember-cli/lib/broccoli/ember-app'); 4 | 5 | module.exports = function (defaults) { 6 | let app = new EmberApp(defaults, { 7 | // Add options here 8 | }); 9 | 10 | // Use `app.import` to add additional libraries to the generated 11 | // output files. 12 | // 13 | // If you need to use different assets in different 14 | // environments, specify an object as the first parameter. That 15 | // object's keys should be the environment name and the values 16 | // should be the asset to use in that environment. 17 | // 18 | // If the library that you are including contains AMD or ES6 19 | // modules that you would like to import into your application 20 | // please specify an object with the list of modules as keys 21 | // along with the exports of each module as its value. 22 | 23 | return app.toTree(); 24 | }; 25 | -------------------------------------------------------------------------------- /test/fixtures/input/lib/special-sauce/addon/components/fire-sauce.js: -------------------------------------------------------------------------------- 1 | import Component from '@ember/component'; 2 | 3 | export default Component.extend({ 4 | }); 5 | -------------------------------------------------------------------------------- /test/fixtures/input/lib/special-sauce/addon/templates/components/fire-sauce.hbs: -------------------------------------------------------------------------------- 1 | {{yield}} -------------------------------------------------------------------------------- /test/fixtures/input/lib/special-sauce/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | name: require('./package').name, 5 | 6 | isDevelopingAddon() { 7 | return true; 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/input/lib/special-sauce/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "special-sauce", 3 | "keywords": [ 4 | "ember-addon" 5 | ], 6 | "dependencies": { 7 | "ember-cli-babel": "*", 8 | "ember-cli-htmlbars": "*" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/fixtures/input/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "input", 3 | "version": "0.0.0", 4 | "private": true, 5 | "description": "Small description for input goes here", 6 | "repository": "", 7 | "license": "MIT", 8 | "author": "", 9 | "directories": { 10 | "doc": "doc", 11 | "test": "tests" 12 | }, 13 | "scripts": { 14 | "build": "ember build --environment=production", 15 | "lint": "npm-run-all --aggregate-output --continue-on-error --parallel \"lint:!(fix)\"", 16 | "lint:fix": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*:fix", 17 | "lint:hbs": "ember-template-lint .", 18 | "lint:hbs:fix": "ember-template-lint . --fix", 19 | "lint:js": "eslint . --cache", 20 | "lint:js:fix": "eslint . --fix", 21 | "start": "ember serve", 22 | "test": "npm-run-all lint test:*", 23 | "test:ember": "ember test" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "^7.21.4", 27 | "@ember/optional-features": "^2.0.0", 28 | "@ember/string": "^3.0.1", 29 | "@ember/test-helpers": "^2.9.3", 30 | "@glimmer/component": "^1.1.2", 31 | "@glimmer/tracking": "^1.1.2", 32 | "babel-eslint": "^10.1.0", 33 | "broccoli-asset-rev": "^3.0.0", 34 | "ember-auto-import": "^2.6.3", 35 | "ember-cli": "~3.28.6", 36 | "ember-cli-app-version": "^6.0.0", 37 | "ember-cli-babel": "^7.26.11", 38 | "ember-cli-dependency-checker": "^3.3.1", 39 | "ember-cli-htmlbars": "^6.2.0", 40 | "ember-cli-inject-live-reload": "^2.1.0", 41 | "ember-cli-sri": "^2.1.1", 42 | "ember-cli-terser": "^4.0.2", 43 | "ember-data": "~3.28.13", 44 | "ember-export-application-global": "^2.0.1", 45 | "ember-fetch": "^8.1.2", 46 | "ember-load-initializers": "^2.1.2", 47 | "ember-maybe-import-regenerator": "^1.0.0", 48 | "ember-page-title": "^7.0.0", 49 | "ember-qunit": "^6.2.0", 50 | "ember-resolver": "^10.0.0", 51 | "ember-source": "~3.28.11", 52 | "ember-template-lint": "^5.7.2", 53 | "eslint": "^8.39.0", 54 | "eslint-config-prettier": "^8.8.0", 55 | "eslint-plugin-ember": "^11.5.2", 56 | "eslint-plugin-node": "^11.1.0", 57 | "eslint-plugin-prettier": "^4.2.1", 58 | "eslint-plugin-qunit": "^7.3.4", 59 | "ember-welcome-page": "^7.0.1", 60 | "loader.js": "^4.7.0", 61 | "npm-run-all": "^4.1.5", 62 | "prettier": "^2.8.8", 63 | "qunit": "^2.19.4", 64 | "qunit-dom": "^2.0.0", 65 | "webpack": "^5.80.0" 66 | }, 67 | "engines": { 68 | "node": ">= 14.*" 69 | }, 70 | "ember": { 71 | "edition": "octane" 72 | }, 73 | "ember-addon": { 74 | "paths": [ 75 | "lib/special-sauce" 76 | ] 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /test/fixtures/input/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /test/fixtures/input/testem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | test_page: 'tests/index.html?hidepassed', 5 | disable_watching: true, 6 | launch_in_ci: ['Chrome'], 7 | launch_in_dev: ['Chrome'], 8 | browser_start_timeout: 120, 9 | browser_args: { 10 | Chrome: { 11 | ci: [ 12 | // --no-sandbox is needed when running Chrome inside a container 13 | process.env.CI ? '--no-sandbox' : null, 14 | '--headless', 15 | '--disable-dev-shm-usage', 16 | '--disable-software-rasterizer', 17 | '--mute-audio', 18 | '--remote-debugging-port=0', 19 | '--window-size=1440,900', 20 | ].filter(Boolean), 21 | }, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /test/fixtures/input/tests/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/input/tests/helpers/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/input/tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Input Tests 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | {{content-for "test-head-footer"}} 19 | 20 | 21 | {{content-for "body"}} 22 | {{content-for "test-body"}} 23 | 24 |
25 |
26 |
27 |
28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | {{content-for "body-footer"}} 38 | {{content-for "test-body-footer"}} 39 | 40 | 41 | -------------------------------------------------------------------------------- /test/fixtures/input/tests/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/input/tests/integration/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/input/tests/integration/components/fire-sauce-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { render } from '@ember/test-helpers'; 4 | import hbs from 'htmlbars-inline-precompile'; 5 | 6 | module('Integration | Component | fire-sauce', function(hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('it renders', async function(assert) { 10 | // Set any properties with this.set('myProperty', 'value'); 11 | // Handle any actions with this.set('myAction', function(val) { ... }); 12 | 13 | await render(hbs``); 14 | 15 | assert.equal(this.element.textContent.trim(), ''); 16 | 17 | // Template block usage: 18 | await render(hbs` 19 | 20 | template block text 21 | 22 | `); 23 | 24 | assert.equal(this.element.textContent.trim(), 'template block text'); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/fixtures/input/tests/integration/components/test-component-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { render } from '@ember/test-helpers'; 4 | import hbs from 'htmlbars-inline-precompile'; 5 | 6 | module('Integration | Component | test-component', function(hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('it renders', async function(assert) { 10 | // Set any properties with this.set('myProperty', 'value'); 11 | // Handle any actions with this.set('myAction', function(val) { ... }); 12 | 13 | await render(hbs``); 14 | 15 | assert.equal(this.element.textContent.trim(), ''); 16 | 17 | // Template block usage: 18 | await render(hbs` 19 | 20 | template block text 21 | 22 | `); 23 | 24 | assert.equal(this.element.textContent.trim(), 'template block text'); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/fixtures/input/tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import Application from 'input/app'; 2 | import config from 'input/config/environment'; 3 | import * as QUnit from 'qunit'; 4 | import { setApplication } from '@ember/test-helpers'; 5 | import { setup } from 'qunit-dom'; 6 | import { start } from 'ember-qunit'; 7 | 8 | setApplication(Application.create(config.APP)); 9 | 10 | setup(QUnit.assert); 11 | 12 | start(); 13 | -------------------------------------------------------------------------------- /test/fixtures/input/tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/input/tests/unit/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/input/vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/input/vendor/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/output/.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 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.hbs] 17 | insert_final_newline = false 18 | 19 | [*.{diff,md}] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /test/fixtures/output/.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/output/.eslintignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | 17 | # ember-try 18 | /.node_modules.ember-try/ 19 | /bower.json.ember-try 20 | /package.json.ember-try 21 | -------------------------------------------------------------------------------- /test/fixtures/output/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | ecmaVersion: 2018, 5 | sourceType: 'module' 6 | }, 7 | plugins: [ 8 | 'ember' 9 | ], 10 | extends: [ 11 | 'eslint:recommended', 12 | 'plugin:ember/recommended' 13 | ], 14 | env: { 15 | browser: true 16 | }, 17 | rules: { 18 | }, 19 | overrides: [ 20 | // node files 21 | { 22 | files: [ 23 | '.eslintrc.js', 24 | '.template-lintrc.js', 25 | 'ember-cli-build.js', 26 | 'testem.js', 27 | 'blueprints/*/index.js', 28 | 'config/**/*.js', 29 | 'lib/*/index.js', 30 | 'server/**/*.js' 31 | ], 32 | parserOptions: { 33 | sourceType: 'script', 34 | ecmaVersion: 2015 35 | }, 36 | env: { 37 | browser: false, 38 | node: true 39 | }, 40 | plugins: ['node'], 41 | rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, { 42 | // add your custom rules and overrides for node files here 43 | 44 | // this can be removed once the following is fixed 45 | // https://github.com/mysticatea/eslint-plugin-node/issues/77 46 | 'node/no-unpublished-require': 'off' 47 | }) 48 | } 49 | ] 50 | }; 51 | -------------------------------------------------------------------------------- /test/fixtures/output/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist/ 5 | /tmp/ 6 | 7 | # dependencies 8 | /bower_components/ 9 | /node_modules/ 10 | 11 | # misc 12 | /.env* 13 | /.pnp* 14 | /.sass-cache 15 | /connect.lock 16 | /coverage/ 17 | /libpeerconnection.log 18 | /npm-debug.log* 19 | /testem.log 20 | /yarn-error.log 21 | 22 | # ember-try 23 | /.node_modules.ember-try/ 24 | /bower.json.ember-try 25 | /package.json.ember-try 26 | -------------------------------------------------------------------------------- /test/fixtures/output/.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended' 5 | }; 6 | -------------------------------------------------------------------------------- /test/fixtures/output/.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | - "8" 5 | 6 | sudo: false 7 | dist: trusty 8 | 9 | addons: 10 | chrome: stable 11 | 12 | cache: 13 | yarn: true 14 | 15 | env: 16 | global: 17 | # See https://git.io/vdao3 for details. 18 | - JOBS=1 19 | 20 | before_install: 21 | - curl -o- -L https://yarnpkg.com/install.sh | bash 22 | - export PATH=$HOME/.yarn/bin:$PATH 23 | 24 | install: 25 | - yarn install --non-interactive 26 | 27 | script: 28 | - yarn lint:hbs 29 | - yarn lint:js 30 | - yarn test 31 | -------------------------------------------------------------------------------- /test/fixtures/output/.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/output/README.md: -------------------------------------------------------------------------------- 1 | # input 2 | 3 | This README outlines the details of collaborating on this Ember application. 4 | A short introduction of this app could easily go here. 5 | 6 | ## Prerequisites 7 | 8 | You will need the following things properly installed on your computer. 9 | 10 | * [Git](https://git-scm.com/) 11 | * [Node.js](https://nodejs.org/) 12 | * [Yarn](https://yarnpkg.com/) 13 | * [Ember CLI](https://ember-cli.com/) 14 | * [Google Chrome](https://google.com/chrome/) 15 | 16 | ## Installation 17 | 18 | * `git clone ` this repository 19 | * `cd input` 20 | * `yarn install` 21 | 22 | ## Running / Development 23 | 24 | * `ember serve` 25 | * Visit your app at [http://localhost:4200](http://localhost:4200). 26 | * Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests). 27 | 28 | ### Code Generators 29 | 30 | Make use of the many generators for code, try `ember help generate` for more details 31 | 32 | ### Running Tests 33 | 34 | * `ember test` 35 | * `ember test --server` 36 | 37 | ### Linting 38 | 39 | * `yarn lint:hbs` 40 | * `yarn lint:js` 41 | * `yarn lint:js --fix` 42 | 43 | ### Building 44 | 45 | * `ember build` (development) 46 | * `ember build --environment production` (production) 47 | 48 | ### Deploying 49 | 50 | Specify what it takes to deploy your app. 51 | 52 | ## Further Reading / Useful Links 53 | 54 | * [ember.js](https://emberjs.com/) 55 | * [ember-cli](https://ember-cli.com/) 56 | * Development Browser Extensions 57 | * [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi) 58 | * [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/) 59 | -------------------------------------------------------------------------------- /test/fixtures/output/app/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from 'ember-resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from 'input/config/environment'; 5 | 6 | export default class App extends Application { 7 | modulePrefix = config.modulePrefix; 8 | podModulePrefix = config.podModulePrefix; 9 | Resolver = Resolver; 10 | } 11 | 12 | loadInitializers(App, config.modulePrefix); 13 | -------------------------------------------------------------------------------- /test/fixtures/output/app/components/test-component.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import { computed } from '@ember/object'; 3 | import Component from '@ember/component'; 4 | 5 | function fullNameMacro() { 6 | return computed('firstName', 'lastName', function() { 7 | return `${this.firstName} ${this.lastName}`; 8 | }); 9 | } 10 | 11 | @classic 12 | export default class TestComponent extends Component { 13 | @fullNameMacro() 14 | fullName; 15 | } 16 | -------------------------------------------------------------------------------- /test/fixtures/output/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/output/app/controllers/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/output/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/output/app/helpers/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/output/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Input 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} 16 | 17 | 18 | {{content-for "body"}} 19 | 20 | 21 | 22 | 23 | {{content-for "body-footer"}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /test/fixtures/output/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/output/app/models/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/output/app/router.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import EmberRouter from '@ember/routing/router'; 3 | import config from './config/environment'; 4 | 5 | @classic 6 | class Router extends EmberRouter { 7 | location = config.locationType; 8 | rootURL = config.rootURL; 9 | } 10 | 11 | Router.map(function () {}); 12 | 13 | export default Router; 14 | -------------------------------------------------------------------------------- /test/fixtures/output/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/output/app/routes/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/output/app/styles/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/output/app/styles/app.css -------------------------------------------------------------------------------- /test/fixtures/output/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | {{page-title "Input"}} 2 | 3 | {{!-- The following component displays Ember's default welcome message. --}} 4 | 5 | {{!-- Feel free to remove this! --}} 6 | 7 | {{outlet}} -------------------------------------------------------------------------------- /test/fixtures/output/app/templates/components/test-component.hbs: -------------------------------------------------------------------------------- 1 | {{yield}} -------------------------------------------------------------------------------- /test/fixtures/output/app/utils/module-with-error.js: -------------------------------------------------------------------------------- 1 | throw new Error('This should not fail the telemetry gathering!'); 2 | -------------------------------------------------------------------------------- /test/fixtures/output/config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(environment) { 4 | let ENV = { 5 | modulePrefix: 'input', 6 | environment, 7 | rootURL: '/', 8 | locationType: 'auto', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true 13 | }, 14 | EXTEND_PROTOTYPES: { 15 | // Prevent Ember Data from overriding Date.parse. 16 | Date: false 17 | } 18 | }, 19 | 20 | APP: { 21 | // Here you can pass flags/options to your application instance 22 | // when it is created 23 | } 24 | }; 25 | 26 | if (environment === 'development') { 27 | // ENV.APP.LOG_RESOLVER = true; 28 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 29 | // ENV.APP.LOG_TRANSITIONS = true; 30 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 31 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 32 | } 33 | 34 | if (environment === 'test') { 35 | // Testem prefers this... 36 | ENV.locationType = 'none'; 37 | 38 | // keep test console output quieter 39 | ENV.APP.LOG_ACTIVE_GENERATION = false; 40 | ENV.APP.LOG_VIEW_LOOKUPS = false; 41 | 42 | ENV.APP.rootElement = '#ember-testing'; 43 | ENV.APP.autoboot = false; 44 | } 45 | 46 | if (environment === 'production') { 47 | // here you can enable a production-specific feature 48 | } 49 | 50 | return ENV; 51 | }; 52 | -------------------------------------------------------------------------------- /test/fixtures/output/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "jquery-integration": true 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/output/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const browsers = [ 4 | 'last 1 Chrome versions', 5 | 'last 1 Firefox versions', 6 | 'last 1 Safari versions' 7 | ]; 8 | 9 | const isCI = !!process.env.CI; 10 | const isProduction = process.env.EMBER_ENV === 'production'; 11 | 12 | if (isCI || isProduction) { 13 | browsers.push('ie 11'); 14 | } 15 | 16 | module.exports = { 17 | browsers 18 | }; 19 | -------------------------------------------------------------------------------- /test/fixtures/output/ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EmberApp = require('ember-cli/lib/broccoli/ember-app'); 4 | 5 | module.exports = function(defaults) { 6 | let app = new EmberApp(defaults, { 7 | // Add options here 8 | }); 9 | 10 | // Use `app.import` to add additional libraries to the generated 11 | // output files. 12 | // 13 | // If you need to use different assets in different 14 | // environments, specify an object as the first parameter. That 15 | // object's keys should be the environment name and the values 16 | // should be the asset to use in that environment. 17 | // 18 | // If the library that you are including contains AMD or ES6 19 | // modules that you would like to import into your application 20 | // please specify an object with the list of modules as keys 21 | // along with the exports of each module as its value. 22 | 23 | return app.toTree(); 24 | }; 25 | -------------------------------------------------------------------------------- /test/fixtures/output/lib/special-sauce/addon/components/fire-sauce.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import Component from '@ember/component'; 3 | 4 | @classic 5 | export default class FireSauce extends Component {} 6 | -------------------------------------------------------------------------------- /test/fixtures/output/lib/special-sauce/addon/templates/components/fire-sauce.hbs: -------------------------------------------------------------------------------- 1 | {{yield}} -------------------------------------------------------------------------------- /test/fixtures/output/lib/special-sauce/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | name: require('./package').name, 5 | 6 | isDevelopingAddon() { 7 | return true; 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /test/fixtures/output/lib/special-sauce/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "special-sauce", 3 | "keywords": [ 4 | "ember-addon" 5 | ], 6 | "dependencies": { 7 | "ember-cli-babel": "*", 8 | "ember-cli-htmlbars": "*" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/fixtures/output/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "input", 3 | "version": "0.0.0", 4 | "private": true, 5 | "description": "Small description for input goes here", 6 | "repository": "", 7 | "license": "MIT", 8 | "author": "", 9 | "directories": { 10 | "doc": "doc", 11 | "test": "tests" 12 | }, 13 | "scripts": { 14 | "build": "ember build", 15 | "lint:hbs": "ember-template-lint .", 16 | "lint:js": "eslint .", 17 | "start": "ember serve", 18 | "test": "ember test" 19 | }, 20 | "devDependencies": { 21 | "@ember/jquery": "^0.6.0", 22 | "@ember/optional-features": "^0.7.0", 23 | "broccoli-asset-rev": "^3.0.0", 24 | "ember-ajax": "^5.0.0", 25 | "ember-cli": "~3.10.1", 26 | "ember-cli-app-version": "^3.2.0", 27 | "ember-cli-babel": "^7.7.3", 28 | "ember-cli-dependency-checker": "^3.1.0", 29 | "ember-cli-eslint": "^5.1.0", 30 | "ember-cli-htmlbars": "^3.0.1", 31 | "ember-cli-htmlbars-inline-precompile": "^2.1.0", 32 | "ember-cli-inject-live-reload": "^1.8.2", 33 | "ember-cli-sri": "^2.1.1", 34 | "ember-cli-template-lint": "^1.0.0-beta.1", 35 | "ember-cli-uglify": "^2.1.0", 36 | "ember-data": "~3.10.0", 37 | "ember-export-application-global": "^2.0.0", 38 | "ember-load-initializers": "^2.0.0", 39 | "ember-maybe-import-regenerator": "^0.1.6", 40 | "ember-qunit": "^4.4.1", 41 | "ember-resolver": "^5.0.1", 42 | "ember-source": "~3.10.0", 43 | "ember-welcome-page": "^4.0.0", 44 | "eslint-plugin-ember": "^6.2.0", 45 | "eslint-plugin-node": "^9.0.1", 46 | "loader.js": "^4.7.0", 47 | "qunit-dom": "^0.8.4" 48 | }, 49 | "engines": { 50 | "node": "8.* || >= 10.*" 51 | }, 52 | "ember-addon": { 53 | "paths": [ 54 | "lib/special-sauce" 55 | ] 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /test/fixtures/output/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /test/fixtures/output/testem.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | test_page: 'tests/index.html?hidepassed', 3 | disable_watching: true, 4 | launch_in_ci: [ 5 | 'Chrome' 6 | ], 7 | launch_in_dev: [ 8 | 'Chrome' 9 | ], 10 | browser_args: { 11 | Chrome: { 12 | ci: [ 13 | // --no-sandbox is needed when running Chrome inside a container 14 | process.env.CI ? '--no-sandbox' : null, 15 | '--headless', 16 | '--disable-gpu', 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 | -------------------------------------------------------------------------------- /test/fixtures/output/tests/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/output/tests/helpers/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/output/tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Input Tests 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | {{content-for "test-head-footer"}} 19 | 20 | 21 | {{content-for "body"}} 22 | {{content-for "test-body"}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{content-for "body-footer"}} 31 | {{content-for "test-body-footer"}} 32 | 33 | 34 | -------------------------------------------------------------------------------- /test/fixtures/output/tests/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/output/tests/integration/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/output/tests/integration/components/fire-sauce-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { render } from '@ember/test-helpers'; 4 | import hbs from 'htmlbars-inline-precompile'; 5 | 6 | module('Integration | Component | fire-sauce', function(hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('it renders', async function(assert) { 10 | // Set any properties with this.set('myProperty', 'value'); 11 | // Handle any actions with this.set('myAction', function(val) { ... }); 12 | 13 | await render(hbs``); 14 | 15 | assert.equal(this.element.textContent.trim(), ''); 16 | 17 | // Template block usage: 18 | await render(hbs` 19 | 20 | template block text 21 | 22 | `); 23 | 24 | assert.equal(this.element.textContent.trim(), 'template block text'); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/fixtures/output/tests/integration/components/test-component-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { render } from '@ember/test-helpers'; 4 | import hbs from 'htmlbars-inline-precompile'; 5 | 6 | module('Integration | Component | test-component', function(hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('it renders', async function(assert) { 10 | // Set any properties with this.set('myProperty', 'value'); 11 | // Handle any actions with this.set('myAction', function(val) { ... }); 12 | 13 | await render(hbs``); 14 | 15 | assert.equal(this.element.textContent.trim(), ''); 16 | 17 | // Template block usage: 18 | await render(hbs` 19 | 20 | template block text 21 | 22 | `); 23 | 24 | assert.equal(this.element.textContent.trim(), 'template block text'); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /test/fixtures/output/tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import Application from '../app'; 2 | import config from '../config/environment'; 3 | import { setApplication } from '@ember/test-helpers'; 4 | import { start } from 'ember-qunit'; 5 | 6 | setApplication(Application.create(config.APP)); 7 | 8 | start(); 9 | -------------------------------------------------------------------------------- /test/fixtures/output/tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/output/tests/unit/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/output/vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/test/fixtures/output/vendor/.gitkeep -------------------------------------------------------------------------------- /test/helpers/expect-multiline.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Makes a regexp pattern to match multiline strings. String arguments passed to 3 | * `makeMultilineMatcher` will be escaped then merged together into a regexp 4 | * that will match partial lines of multi-line error messages when paired with 5 | * Jest `expect().toThrow` or `expect.stringMatching`. 6 | * 7 | * @example 8 | * ``` 9 | * const expected = makeMultilineMatcher('Line 1', 'Line 2', '3') 10 | * //=> 'Line 1[\S\s]*Line 2[\S\s]*3' 11 | * 12 | * expect('Line 1\nLine 2\nLine 3').toEqual(expect.stringMatching(expected)); 13 | * //=> passes 14 | * ``` 15 | */ 16 | export function makeMultilineMatcher(...parts: string[]): string { 17 | return parts.map(escapeRegExp).join('[\\S\\s]*'); 18 | } 19 | 20 | /** 21 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping 22 | */ 23 | function escapeRegExp(string: string): string { 24 | return string.replace(/[$()*+.?[\\\]^{|}]/g, '\\$&'); // $& means the whole matched string 25 | } 26 | -------------------------------------------------------------------------------- /test/run-test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | const { spawn } = require('child_process'); 4 | const execa = require('execa'); 5 | const path = require('path'); 6 | 7 | // resolved from the root of the project 8 | const inputDir = path.resolve('./test/fixtures/input'); 9 | const execOpts = { cwd: inputDir, stderr: 'inherit' }; 10 | 11 | (async () => { 12 | console.log('installing deps'); 13 | 14 | await execa('rm', ['-rf', 'node_modules'], execOpts); 15 | await execa('yarn', ['install'], execOpts); 16 | 17 | console.log('starting serve'); 18 | 19 | // We use spawn for this one so we can kill it later without throwing an error 20 | const emberServe = spawn('yarn', ['start'], execOpts); 21 | emberServe.stderr.pipe(process.stderr); 22 | 23 | await new Promise((resolve) => { 24 | emberServe.stdout.on('data', (data) => { 25 | if (data.toString().includes('Build successful')) { 26 | resolve(); 27 | } 28 | }); 29 | }); 30 | 31 | console.log('running codemod'); 32 | 33 | const codemodProcess = execa( 34 | '../../../bin/cli.js', 35 | ['http://localhost:4200', 'app', 'lib/special-sauce/addon'], 36 | execOpts 37 | ); 38 | codemodProcess.stdout.pipe(process.stdout); 39 | 40 | await codemodProcess; 41 | 42 | console.log('codemod complete, ending serve'); 43 | 44 | emberServe.kill('SIGTERM'); 45 | 46 | console.log('comparing results'); 47 | 48 | try { 49 | await execa('diff', ['-rq', './app', '../output/app'], execOpts); 50 | await execa('diff', ['-rq', './lib', '../output/lib'], execOpts); 51 | } catch (e) { 52 | console.error('codemod did not run successfully'); 53 | console.log(e.stdout); 54 | 55 | process.exit(1); 56 | } 57 | 58 | console.log('codemod ran successfully! 🎉'); 59 | process.exit(0); 60 | })().catch((e) => { 61 | console.error(e); 62 | process.exit(1); 63 | }); 64 | -------------------------------------------------------------------------------- /transforms/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ember-codemods/ember-native-class-codemod/aac510c50e8c4f0879ba9d932ff81c6730abd7db/transforms/.gitkeep -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/-mock-telemetry.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtime": { 3 | "computedProperties": ["computedMacro", "anotherMacro", "numPlusOne", "numPlusPlus", "error", "errorService"], 4 | "observedProperties": ["prop"], 5 | "observerProperties": { "observerProp": ["prop"] }, 6 | "offProperties": { "offProp": ["prop1", "prop2"] }, 7 | "overriddenActions": ["overriddenActionMethod"], 8 | "overriddenProperties": ["overriddenMethod"], 9 | "ownProperties": [], 10 | "type": "EmberObject", 11 | "unobservedProperties": { "unobservedProp": ["prop3", "prop4"] } 12 | }, 13 | "ember-concurrency": { 14 | "computedProperties": ["fetchAlerts"], 15 | "observedProperties": [], 16 | "observerProperties": {}, 17 | "offProperties": {}, 18 | "overriddenActions": [], 19 | "overriddenProperties": [], 20 | "ownProperties": [], 21 | "type": "Component", 22 | "unobservedProperties": {} 23 | }, 24 | "injecting-service": { 25 | "computedProperties": ["something", "otherThing"], 26 | "type": "Service", 27 | "unobservedProperties": {} 28 | }, 29 | "types/components/basic": { 30 | "type": "Component" 31 | }, 32 | "types/controllers/basic": { 33 | "type": "Controller" 34 | }, 35 | "types/helpers/basic": { 36 | "type": "Helper" 37 | }, 38 | "types/routes/basic": { 39 | "type": "Route" 40 | }, 41 | "types/services/basic": { 42 | "type": "Service" 43 | }, 44 | "types/services/some-service": { 45 | "type": "Service" 46 | }, 47 | "types/pods/foo/controller": { 48 | "type": "Controller" 49 | }, 50 | "object-literal-with-decorators-invalid-macro": { 51 | "computedProperties": ["computedMacro"], 52 | "type": "EmberObject" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/action-invalid-1.input.js: -------------------------------------------------------------------------------- 1 | const Foo1 = EmberObject.extend({ 2 | actions: { 3 | bar() { 4 | this._super(...arguments); 5 | this.get('bar')(); 6 | }, 7 | }, 8 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/action-invalid-1.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo1': 4 | [actions]: Transform not supported - [bar]: calling the passed action would cause an infinite loop. See https://github.com/scalvert/eslint-plugin-ember-es6-class/pull/2 for more details 5 | */ 6 | 7 | const Foo1 = EmberObject.extend({ 8 | actions: { 9 | bar() { 10 | this._super(...arguments); 11 | this.get('bar')(); 12 | }, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/action-invalid-2.input.js: -------------------------------------------------------------------------------- 1 | const Foo2 = EmberObject.extend({ 2 | actions: { 3 | biz() { 4 | this._super(...arguments); 5 | get(this, 'biz')(); 6 | }, 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/action-invalid-2.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo2': 4 | [actions]: Transform not supported - [biz]: calling the passed action would cause an infinite loop. See https://github.com/scalvert/eslint-plugin-ember-es6-class/pull/2 for more details 5 | */ 6 | 7 | const Foo2 = EmberObject.extend({ 8 | actions: { 9 | biz() { 10 | this._super(...arguments); 11 | get(this, 'biz')(); 12 | }, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/action-invalid-3.input.js: -------------------------------------------------------------------------------- 1 | const Foo3 = EmberObject.extend({ 2 | actions: { 3 | baz() { 4 | this._super(...arguments); 5 | tryInvoke(this, 'baz'); 6 | }, 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/action-invalid-3.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo3': 4 | [actions]: Transform not supported - [baz]: calling the passed action would cause an infinite loop. See https://github.com/scalvert/eslint-plugin-ember-es6-class/pull/2 for more details 5 | */ 6 | 7 | const Foo3 = EmberObject.extend({ 8 | actions: { 9 | baz() { 10 | this._super(...arguments); 11 | tryInvoke(this, 'baz'); 12 | }, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/action-invalid-4.input.js: -------------------------------------------------------------------------------- 1 | const Foo4 = EmberObject.extend({ 2 | actions: { 3 | sendBaz() { 4 | this._super(...arguments); 5 | this.send('sendBaz'); 6 | }, 7 | }, 8 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/action-invalid-4.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo4': 4 | [actions]: Transform not supported - [sendBaz]: calling the passed action would cause an infinite loop. See https://github.com/scalvert/eslint-plugin-ember-es6-class/pull/2 for more details 5 | */ 6 | 7 | const Foo4 = EmberObject.extend({ 8 | actions: { 9 | sendBaz() { 10 | this._super(...arguments); 11 | this.send('sendBaz'); 12 | }, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/action-invalid-5.input.js: -------------------------------------------------------------------------------- 1 | const Foo5 = EmberObject.extend({ 2 | actions: { 3 | thisBaz() { 4 | this._super(...arguments); 5 | this.thisBaz(); 6 | }, 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/action-invalid-5.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo5': 4 | [actions]: Transform not supported - [thisBaz]: calling the passed action would cause an infinite loop. See https://github.com/scalvert/eslint-plugin-ember-es6-class/pull/2 for more details 5 | */ 6 | 7 | const Foo5 = EmberObject.extend({ 8 | actions: { 9 | thisBaz() { 10 | this._super(...arguments); 11 | this.thisBaz(); 12 | }, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/async-function-property.input.js: -------------------------------------------------------------------------------- 1 | const Foo = Test.extend({ 2 | myAsyncMethod: async function() { 3 | await Promise.resolve('hello'); 4 | } 5 | }); 6 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/async-function-property.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | 3 | @classic 4 | class Foo extends Test { 5 | async myAsyncMethod() { 6 | await Promise.resolve('hello'); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/async-method.input.js: -------------------------------------------------------------------------------- 1 | const Foo = Test.extend({ 2 | async myAsyncMethod() { 3 | await Promise.resolve('hello'); 4 | } 5 | }); 6 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/async-method.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | 3 | @classic 4 | class Foo extends Test { 5 | async myAsyncMethod() { 6 | await Promise.resolve('hello'); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/basic-computed.input.js: -------------------------------------------------------------------------------- 1 | import { computed } from '@ember/object'; 2 | 3 | var HasComputed = EmberObject.extend({ 4 | isEnabled: computed('a', 'c', function() { 5 | return false; 6 | }), 7 | a: true, 8 | c: '' 9 | }); 10 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/basic-computed.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import { computed } from '@ember/object'; 3 | 4 | @classic 5 | class HasComputed extends EmberObject { 6 | @computed('a', 'c') 7 | get isEnabled() { 8 | return false; 9 | } 10 | 11 | a = true; 12 | c = ''; 13 | } 14 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/basic.input.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Program comments 3 | */ 4 | const Foo1 = Test.extend(MyMixin, { 5 | /** 6 | * Property comments 7 | */ 8 | prop: 'defaultValue', 9 | boolProp: true, 10 | numProp: 123, 11 | [MY_VAL]: 'val', 12 | queryParams: {}, 13 | someVal, 14 | 15 | /** 16 | * Method comments 17 | */ 18 | method() { 19 | // do things 20 | }, 21 | 22 | otherMethod: function() {}, 23 | 24 | get accessor() { 25 | return this._value; 26 | }, 27 | 28 | set accessor(value) { 29 | this._value = value; 30 | }, 31 | 32 | anotherMethod() { 33 | this._super(...arguments); 34 | }, 35 | }); 36 | 37 | const Foo2 = EmberObject.extend(MixinA, MixinB); 38 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/basic.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | 3 | /** 4 | * Program comments 5 | */ 6 | @classic 7 | class Foo1 extends Test.extend(MyMixin) { 8 | /** 9 | * Property comments 10 | */ 11 | prop = 'defaultValue'; 12 | 13 | boolProp = true; 14 | numProp = 123; 15 | [MY_VAL] = 'val'; 16 | queryParams = {}; 17 | someVal = someVal; 18 | 19 | /** 20 | * Method comments 21 | */ 22 | method() { 23 | // do things 24 | } 25 | 26 | otherMethod() {} 27 | 28 | get accessor() { 29 | return this._value; 30 | } 31 | 32 | set accessor(value) { 33 | this._value = value; 34 | } 35 | 36 | anotherMethod() { 37 | super.anotherMethod(...arguments); 38 | } 39 | } 40 | 41 | @classic 42 | class Foo2 extends EmberObject.extend(MixinA, MixinB) {} 43 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/chained-class-definition.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject from '@ember/object'; 2 | 3 | export default EmberObject.extend({}).reopenClass({}); 4 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/chained-class-definition.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'ChainedClassDefinition': 4 | class has chained definition (e.g. EmberObject.extend().reopenClass(); 5 | */ 6 | 7 | import EmberObject from '@ember/object'; 8 | 9 | export default EmberObject.extend({}).reopenClass({}); 10 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/class-fields.input.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Program comments 3 | */ 4 | const Foo = Test.extend({ 5 | /** 6 | * Method comments 7 | */ 8 | method() { 9 | // do things 10 | }, 11 | 12 | otherMethod: function() {}, 13 | 14 | get accessor() { 15 | return this._value; 16 | }, 17 | 18 | set accessor(value) { 19 | this._value = value; 20 | }, 21 | 22 | anotherMethod() { 23 | this._super(...arguments); 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/class-fields.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | 3 | /** 4 | * Program comments 5 | */ 6 | @classic 7 | class Foo extends Test { 8 | /** 9 | * Method comments 10 | */ 11 | method() { 12 | // do things 13 | } 14 | 15 | otherMethod() {} 16 | 17 | get accessor() { 18 | return this._value; 19 | } 20 | 21 | set accessor(value) { 22 | this._value = value; 23 | } 24 | 25 | anotherMethod() { 26 | super.anotherMethod(...arguments); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/class-reopen.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject from '@ember/object'; 2 | 3 | const Foo = EmberObject.extend({}); 4 | 5 | Foo.reopenClass({}); 6 | 7 | export default Foo; 8 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/class-reopen.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import EmberObject from '@ember/object'; 3 | 4 | @classic 5 | class Foo extends EmberObject {} 6 | 7 | Foo.reopenClass({}); 8 | 9 | export default Foo; 10 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-1.input.js: -------------------------------------------------------------------------------- 1 | // Do not transform 2 | const Foo1 = EmberObject.extend({ 3 | statefulObject: {}, 4 | }); 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-1.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo1': 4 | [statefulObject]: Transform not supported - value is of type object. For more details: eslint-plugin-ember/avoid-leaking-state-in-ember-objects 5 | */ 6 | 7 | // Do not transform 8 | const Foo1 = EmberObject.extend({ 9 | statefulObject: {}, 10 | }); 11 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-2.input.js: -------------------------------------------------------------------------------- 1 | // Do not transform if not a primitive value 2 | const Foo2 = EmberObject.extend({ 3 | macroValue: macro(), 4 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-2.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "noTelemetry": "false" 3 | } -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-2.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo2': 4 | [macroValue]: Transform not supported - call to 'macro' can not be transformed 5 | */ 6 | 7 | // Do not transform if not a primitive value 8 | const Foo2 = EmberObject.extend({ 9 | macroValue: macro(), 10 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-3.input.js: -------------------------------------------------------------------------------- 1 | import { computed } from '@ember/object'; 2 | 3 | // Do not transform as a computed property has readOnly and volatile with meta 4 | const Foo3 = EmberObject.extend({ 5 | firstName: '', 6 | lastName: '', 7 | 8 | fName2: computed('firstName', 'lastName', function() { 9 | return true; 10 | }) 11 | .property('baz') 12 | .readOnly() 13 | .volatile() 14 | .meta({ type: 'Property' }), 15 | }); 16 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-3.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo3': 4 | [fName2]: Transform not supported - value has modifiers like 'property' or 'meta' 5 | */ 6 | 7 | import { computed } from '@ember/object'; 8 | 9 | // Do not transform as a computed property has readOnly and volatile with meta 10 | const Foo3 = EmberObject.extend({ 11 | firstName: '', 12 | lastName: '', 13 | 14 | fName2: computed('firstName', 'lastName', function() { 15 | return true; 16 | }) 17 | .property('baz') 18 | .readOnly() 19 | .volatile() 20 | .meta({ type: 'Property' }), 21 | }); 22 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-4.input.js: -------------------------------------------------------------------------------- 1 | // Do not transform as a computed meta has volatile 2 | const Foo4 = EmberObject.extend({ 3 | lName1: add('description', 'lastName').volatile(), 4 | }); 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-4.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "noTelemetry": "false" 3 | } -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-4.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo4': 4 | [lName1]: Transform not supported - call to 'add' can not be transformed 5 | */ 6 | 7 | // Do not transform as a computed meta has volatile 8 | const Foo4 = EmberObject.extend({ 9 | lName1: add('description', 'lastName').volatile(), 10 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-5.input.js: -------------------------------------------------------------------------------- 1 | import { computed } from '@ember/object'; 2 | 3 | // Do not transform as computed prop has `property` 4 | const Foo5 = EmberObject.extend({ 5 | fName2: computed('firstName', 'lastName', function() { 6 | return true; 7 | }).property('baz'), 8 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-5.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo5': 4 | [fName2]: Transform not supported - value has modifiers like 'property' or 'meta' 5 | */ 6 | 7 | import { computed } from '@ember/object'; 8 | 9 | // Do not transform as computed prop has `property` 10 | const Foo5 = EmberObject.extend({ 11 | fName2: computed('firstName', 'lastName', function() { 12 | return true; 13 | }).property('baz'), 14 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-6.input.js: -------------------------------------------------------------------------------- 1 | import { computed } from '@ember/object'; 2 | 3 | // Do not transform as computed prop has `meta` 4 | const Foo6 = EmberObject.extend({ 5 | fName2: computed('firstName', 'lastName', function() { 6 | return true; 7 | }).meta({ type: 'Property' }), 8 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-6.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo6': 4 | [fName2]: Transform not supported - value has modifiers like 'property' or 'meta' 5 | */ 6 | 7 | import { computed } from '@ember/object'; 8 | 9 | // Do not transform as computed prop has `meta` 10 | const Foo6 = EmberObject.extend({ 11 | fName2: computed('firstName', 'lastName', function() { 12 | return true; 13 | }).meta({ type: 'Property' }), 14 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-7.input.js: -------------------------------------------------------------------------------- 1 | // Do not transform as action name matches lifecycle hook 2 | const Foo7 = EmberObject.extend({ 3 | actions: { 4 | click() { 5 | this.set('clicked', true); 6 | }, 7 | }, 8 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-7.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo7': 4 | [actions]: Transform not supported - [click]: action name matches one of the lifecycle hooks. Rename and try again. See https://github.com/scalvert/ember-native-class-codemod/issues/34 for more details 5 | */ 6 | 7 | // Do not transform as action name matches lifecycle hook 8 | const Foo7 = EmberObject.extend({ 9 | actions: { 10 | click() { 11 | this.set('clicked', true); 12 | }, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-8.input.js: -------------------------------------------------------------------------------- 1 | const Foo8 = EmberObject.extend({ 2 | statefulArray: [], 3 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators-invalid-8.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo8': 4 | [statefulArray]: Transform not supported - value is of type object. For more details: eslint-plugin-ember/avoid-leaking-state-in-ember-objects 5 | */ 6 | 7 | const Foo8 = EmberObject.extend({ 8 | statefulArray: [], 9 | }); 10 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/decorators.input.js: -------------------------------------------------------------------------------- 1 | import { 2 | alias, 3 | gt, 4 | equal, 5 | readOnly, 6 | reads, 7 | oneWay as enoWay, 8 | sum as add, 9 | map as computedMap, 10 | filter, 11 | } from '@ember/object/computed'; 12 | import { get, set, observer as watcher, computed } from '@ember/object'; 13 | import { inject as controller } from '@ember/controller'; 14 | import { inject as service } from '@ember/service'; 15 | import { on } from '@ember/object/evented'; 16 | import layout from 'components/templates/foo'; 17 | import { someActionUtil } from 'some/action/util'; 18 | import NUMERIC_CONSTANT from 'constants/numbers'; 19 | 20 | const Foo1 = EmberObject.extend({ 21 | tagName: 'div', 22 | classNames: ['test-class', 'custom-class'], 23 | a: '', 24 | b: service('store'), 25 | myController: controller('abc'), 26 | observedProp: watcher('xyz', function() { 27 | return 'observed'; 28 | }), 29 | observedProp2: watcher('xyz', function() { 30 | return this._super(...arguments); 31 | }), 32 | event: on('click', function() { 33 | return 'abc'; 34 | }), 35 | excitingChores: computedMap('chores', function(chore, index) { 36 | return chore.toUpperCase() + '!'; 37 | }), 38 | remainingChores: filter('chores', function(chore, index, array) { 39 | return !chore.done; 40 | }), 41 | 42 | actions: { 43 | someActionUtil, 44 | /** 45 | Comments 46 | */ 47 | bar(temp1) {}, 48 | baz() { 49 | this._super(...arguments); 50 | }, 51 | biz() {}, 52 | }, 53 | }); 54 | 55 | var comp = EmberObject.extend({ 56 | classNameBindings: ['isEnabled:enabled:disabled', 'a:b:c', 'c:d'], 57 | isEnabled: computed('a', 'c', function() { 58 | return false; 59 | }), 60 | a: true, 61 | c: '', 62 | attributeBindings: ['customHref:href'], 63 | 64 | customHref: 'http://emberjs.com', 65 | }); 66 | 67 | const Foo2 = EmberObject.extend({ 68 | firstName: null, 69 | lastName: null, 70 | 71 | /** 72 | Computed fullname 73 | */ 74 | fullName: computed('firstName', 'lastName', { 75 | get(key) { 76 | return this._super(...arguments) && `${this.get('firstName')} ${this.get('lastName')}`; 77 | }, 78 | set(key, value) { 79 | let [firstName, lastName] = value.split(/\s+/); 80 | this.set('firstName', firstName); 81 | this.set('lastName', lastName); 82 | return value; 83 | }, 84 | }).readOnly(), 85 | /** 86 | Computed description 87 | */ 88 | description: computed('fullName', 'age', 'country', function() { 89 | const desc = this._super(...arguments); 90 | if (desc) { 91 | return desc; 92 | } 93 | return `${this.get('fullName')}; Age: ${this.get('age')}; Country: ${this.get('country')}`; 94 | }) 95 | .volatile() 96 | .readOnly(), 97 | 98 | /** 99 | * Fname 100 | */ 101 | fName: alias('firstName'), 102 | 103 | /** 104 | * Fname1 105 | */ 106 | fName1: alias('firstName'), 107 | 108 | /** 109 | * Fname2 110 | */ 111 | fName2: computed('firstName', 'lastName', function() { 112 | return true; 113 | }).readOnly(), 114 | 115 | /** 116 | * Fname3 117 | */ 118 | fName3: computed('firstName', 'lastName', function() { 119 | return true; 120 | }).volatile(), 121 | 122 | /** 123 | * Lname 124 | */ 125 | lName: alias('firstName', 'lastName').readOnly(), 126 | 127 | /** 128 | * Lname1 129 | */ 130 | lName1: add('description', 'lastName'), 131 | 132 | /** 133 | * Lname2 134 | */ 135 | lName2: readOnly('description'), 136 | 137 | /** 138 | * Lname3 139 | */ 140 | lName3: reads('description', 'lastName'), 141 | 142 | /** 143 | * Lname4 144 | */ 145 | lName4: enoWay('description', 'lastName'), 146 | 147 | /** 148 | * Lname5 149 | */ 150 | lName5: add('description', 'lastName').readOnly(), 151 | 152 | isEqualToLimit: equal('limit', NUMERIC_CONSTANT.LIMIT).readOnly(), 153 | 154 | isGreaterThanLimit: gt('limit', NUMERIC_CONSTANT).readOnly(), 155 | }); 156 | 157 | const Foo3 = EmberObject.extend({ 158 | layout, 159 | }); 160 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/default-export.input.js: -------------------------------------------------------------------------------- 1 | export default EmberObject.extend({}); 2 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/default-export.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | @classic 3 | export default class DefaultExport extends EmberObject {} 4 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/double-quotes.input.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Program comments 3 | */ 4 | const Foo1 = Test.extend(MyMixin, { 5 | /** 6 | * Property comments 7 | */ 8 | prop: "defaultValue", 9 | boolProp: true, 10 | numProp: 123, 11 | [MY_VAL]: "val", 12 | queryParams: {}, 13 | someVal, 14 | 15 | /** 16 | * Method comments 17 | */ 18 | method() { 19 | // do things 20 | }, 21 | 22 | otherMethod: function() {}, 23 | 24 | get accessor() { 25 | return this._value; 26 | }, 27 | 28 | set accessor(value) { 29 | this._value = value; 30 | }, 31 | 32 | anotherMethod() { 33 | this._super(...arguments); 34 | }, 35 | }); 36 | 37 | const Foo2 = EmberObject.extend(MixinA, MixinB); 38 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/double-quotes.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "quote": "double" 3 | } 4 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/double-quotes.output.js: -------------------------------------------------------------------------------- 1 | import classic from "ember-classic-decorator"; 2 | 3 | /** 4 | * Program comments 5 | */ 6 | @classic 7 | class Foo1 extends Test.extend(MyMixin) { 8 | /** 9 | * Property comments 10 | */ 11 | prop = "defaultValue"; 12 | 13 | boolProp = true; 14 | numProp = 123; 15 | [MY_VAL] = "val"; 16 | queryParams = {}; 17 | someVal = someVal; 18 | 19 | /** 20 | * Method comments 21 | */ 22 | method() { 23 | // do things 24 | } 25 | 26 | otherMethod() {} 27 | 28 | get accessor() { 29 | return this._value; 30 | } 31 | 32 | set accessor(value) { 33 | this._value = value; 34 | } 35 | 36 | anotherMethod() { 37 | super.anotherMethod(...arguments); 38 | } 39 | } 40 | 41 | @classic 42 | class Foo2 extends EmberObject.extend(MixinA, MixinB) {} 43 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/ember-concurrency.input.js: -------------------------------------------------------------------------------- 1 | import Component from '@ember/component'; 2 | import { task } from 'ember-concurrency'; 3 | 4 | export default Component.extend({ 5 | fetchAlerts: task(function*() { 6 | let alerts = yield this.store.query('alert', { 7 | filter: { id: this.get('alert.id') } 8 | }); 9 | return alerts.sortBy('createdAt').reverse(); 10 | }).drop(), 11 | }); 12 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/ember-concurrency.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import Component from '@ember/component'; 3 | import { task } from 'ember-concurrency'; 4 | 5 | @classic 6 | export default class EmberConcurrency extends Component { 7 | @(task(function*() { 8 | let alerts = yield this.store.query('alert', { 9 | filter: { id: this.get('alert.id') } 10 | }); 11 | return alerts.sortBy('createdAt').reverse(); 12 | }).drop()) 13 | fetchAlerts; 14 | } 15 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/frozen.input.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Program comments 3 | */ 4 | const Foo = Test.extend({ 5 | frozen: Object.freeze(['name']) 6 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/frozen.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | 3 | /** 4 | * Program comments 5 | */ 6 | @classic 7 | class Foo extends Test { 8 | frozen = Object.freeze(['name']); 9 | } 10 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/generator-method.input.js: -------------------------------------------------------------------------------- 1 | const Foo = Test.extend({ 2 | *gen() { 3 | yield 'hello'; 4 | } 5 | }); 6 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/generator-method.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | 3 | @classic 4 | class Foo extends Test { 5 | *gen() { 6 | yield 'hello'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/generator-property.input.js: -------------------------------------------------------------------------------- 1 | const Foo = Test.extend({ 2 | gen: function*() { 3 | yield 'hello'; 4 | } 5 | }); 6 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/generator-property.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | 3 | @classic 4 | class Foo extends Test { 5 | *gen() { 6 | yield 'hello'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/import.input.js: -------------------------------------------------------------------------------- 1 | import Service from '@ember/service'; 2 | import Controller from '@ember/controller'; 3 | import Evented, { on } from '@ember/object/evented'; 4 | 5 | const ser = Service.extend({}); 6 | const ctrl = Controller.extend({}); 7 | const evt = Service.extend(Evented, { 8 | e: on('click', function() { 9 | return 'e'; 10 | }), 11 | }); 12 | 13 | export { ser, ctrl, evt }; 14 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/import.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import { on } from '@ember-decorators/object'; 3 | import Service from '@ember/service'; 4 | import Controller from '@ember/controller'; 5 | import Evented from '@ember/object/evented'; 6 | 7 | @classic 8 | class ser extends Service {} 9 | 10 | @classic 11 | class ctrl extends Controller {} 12 | 13 | @classic 14 | class evt extends Service.extend(Evented) { 15 | @on('click') 16 | e() { 17 | return 'e'; 18 | } 19 | } 20 | 21 | export { ser, ctrl, evt }; 22 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/inject-basic.input.js: -------------------------------------------------------------------------------- 1 | import { inject as controller } from '@ember/controller'; 2 | import { inject as service } from '@ember/service'; 3 | 4 | const Foo1 = EmberObject.extend({ 5 | b: service('store'), 6 | myController: controller('abc'), 7 | }); 8 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/inject-basic.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import { inject as controller } from '@ember/controller'; 3 | import { inject as service } from '@ember/service'; 4 | 5 | @classic 6 | class Foo1 extends EmberObject { 7 | @service('store') 8 | b; 9 | 10 | @controller('abc') 11 | myController; 12 | } 13 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/injecting-service.input.js: -------------------------------------------------------------------------------- 1 | import Service, { service as injectService } from '@ember/service'; 2 | 3 | export default Service.extend({ 4 | something: injectService(), 5 | otherThing: injectService('some-thing'), 6 | }); 7 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/injecting-service.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import Service, { service as injectService } from '@ember/service'; 3 | 4 | @classic 5 | export default class InjectingService extends Service { 6 | @injectService() 7 | something; 8 | 9 | @injectService('some-thing') 10 | otherThing; 11 | } 12 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/logical-expression.input.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Program comments 3 | */ 4 | const Foo = Test.extend({ 5 | location: ENV.locationType || 'history' 6 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/logical-expression.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | 3 | /** 4 | * Program comments 5 | */ 6 | @classic 7 | class Foo extends Test { 8 | location = ENV.locationType || 'history'; 9 | } 10 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/mixin.input.js: -------------------------------------------------------------------------------- 1 | const HasMixin = Test.extend(MyMixin, {}); 2 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/mixin.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | 3 | @classic 4 | class HasMixin extends Test.extend(MyMixin) {} 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/object-literal-with-action-hash-and-decorator.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject, { action, set } from '@ember/object'; 2 | 3 | const Foo = EmberObject.extend({ 4 | @action 5 | toggleShowing() { 6 | set(this, 'isShowing', !this.isShowing); 7 | }, 8 | 9 | actions: { 10 | toggleSnowing() { 11 | set(this, 'isSnowing', !this.isSnowing); 12 | } 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/object-literal-with-action-hash-and-decorator.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import EmberObject, { action, set } from '@ember/object'; 3 | 4 | @classic 5 | class Foo extends EmberObject { 6 | @action 7 | toggleShowing() { 8 | set(this, 'isShowing', !this.isShowing); 9 | } 10 | 11 | @action 12 | toggleSnowing() { 13 | set(this, 'isSnowing', !this.isSnowing); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/object-literal-with-decorators-invalid-1.input.js: -------------------------------------------------------------------------------- 1 | // Do not transform if not a primitive value 2 | const Foo1 = EmberObject.extend({ 3 | @tracked computedMacro: customMacro(), 4 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/object-literal-with-decorators-invalid-1.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "noTelemetry": false 3 | } -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/object-literal-with-decorators-invalid-1.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo1': 4 | [computedMacro]: Transform not supported - can only transform object literal decorators on methods or properties with literal values (string, number, boolean, null, undefined) 5 | [computedMacro]: Transform not supported - call to 'customMacro' can not be transformed 6 | */ 7 | 8 | // Do not transform if not a primitive value 9 | const Foo1 = EmberObject.extend({ 10 | @tracked computedMacro: customMacro(), 11 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/object-literal-with-decorators-invalid-2.input.js: -------------------------------------------------------------------------------- 1 | // Do not transform if not on allowlist 2 | const Foo2 = EmberObject.extend({ 3 | @banned prop: '', 4 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/object-literal-with-decorators-invalid-2.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "noTelemetry": false 3 | } -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/object-literal-with-decorators-invalid-2.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo2': 4 | [prop]: Transform not supported - decorator '@banned' not included in ALLOWED_OBJECT_LITERAL_DECORATORS or option '--objectLiteralDecorators' 5 | */ 6 | 7 | // Do not transform if not on allowlist 8 | const Foo2 = EmberObject.extend({ 9 | @banned prop: '', 10 | }); 11 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/object-literal-with-decorators-invalid-3.input.js: -------------------------------------------------------------------------------- 1 | // Do not transform array 2 | const Foo3 = EmberObject.extend({ 3 | @tracked arr: [1, 2, 3], 4 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/object-literal-with-decorators-invalid-3.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "noTelemetry": false 3 | } -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/object-literal-with-decorators-invalid-3.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo3': 4 | [arr]: Transform not supported - value is of type object. For more details: eslint-plugin-ember/avoid-leaking-state-in-ember-objects 5 | */ 6 | 7 | // Do not transform array 8 | const Foo3 = EmberObject.extend({ 9 | @tracked arr: [1, 2, 3], 10 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/object-literal-with-decorators-invalid-4.input.js: -------------------------------------------------------------------------------- 1 | // Do not transform function expression if not on allowlist 2 | const Foo4 = EmberObject.extend({ 3 | @userAdded methodish: () => {}, 4 | }); 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/object-literal-with-decorators-invalid-4.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "noTelemetry": false 3 | } -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/object-literal-with-decorators-invalid-4.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo4': 4 | [methodish]: Transform not supported - decorator '@userAdded' not included in ALLOWED_OBJECT_LITERAL_DECORATORS or option '--objectLiteralDecorators' 5 | */ 6 | 7 | // Do not transform function expression if not on allowlist 8 | const Foo4 = EmberObject.extend({ 9 | @userAdded methodish: () => {}, 10 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/object-literal-with-decorators.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject, { action, set, computed } from '@ember/object'; 2 | import { dependentKeyCompat } from '@ember/object/compat'; 3 | import { alias } from '@ember/object/computed'; 4 | import { tracked } from '@glimmer/tracking'; 5 | import { attribute, className } from '@ember-decorators/component'; 6 | import { observes, on } from '@ember-decorators/object'; 7 | 8 | const Foo = EmberObject.extend({ 9 | 10 | // @ember/object 11 | 12 | @action 13 | toggleShowing() { 14 | set(this, 'isShowing', !this.isShowing); 15 | }, 16 | 17 | @computed('firstName', 'lastName') 18 | fullName() { 19 | return `${this.firstName} ${this.lastName}`; 20 | }, 21 | 22 | // @ember/object/compat 23 | 24 | @dependentKeyCompat 25 | fullName2: function() { 26 | return `${this.firstName} ${this.lastName}`; 27 | }, 28 | 29 | // @ember/object/computed 30 | 31 | @alias('foo') hasAlias: undefined, 32 | @and('foo', 'bar') hasAnd: undefined, 33 | @bool('foo') hasBool: undefined, 34 | @collect('foo', 'bar') hasCollect: undefined, 35 | @deprecatingAlias('foo') hasDeprecatingAlias: undefined, 36 | @empty('foo') hasEmpty: undefined, 37 | @equal('foo', 'bar') hasEqual: undefined, 38 | @filterBy('foo', 'bar') hasFilterBy: undefined, 39 | @gt('foo', 'bar') hasGt: undefined, 40 | @gte('foo', 'bar') hasGte: undefined, 41 | @intersect('foo', 'bar') hasIntersect: undefined, 42 | @lt('foo', 'bar') hasLt: undefined, 43 | @lte('foo', 'bar') hasLte: undefined, 44 | @mapBy('foo', 'bar') hasMapBy: undefined, 45 | @match('foo', /bar/) hasMatch: undefined, 46 | @max('foo', 'bar') hasMax: undefined, 47 | @min('foo', 'bar') hasMin: undefined, 48 | @none('foo') hasNone: undefined, 49 | @not('foo') hasNot: undefined, 50 | @notEmpty('foo') hasNotEmpty: undefined, 51 | @oneWay('foo') hasOneWay: undefined, 52 | @or('foo', 'bar') hasOr: undefined, 53 | @readOnly('foo') hasReadOnly: undefined, 54 | @reads('foo') hasReads: undefined, 55 | @setDiff('foo', 'bar') hasSetDiff: undefined, 56 | @sum('foo', 'bar') hasSum: undefined, 57 | @union('foo', 'bar') hasUnion: undefined, 58 | @uniq('foo') hasUniq: undefined, 59 | @uniqBy('foo', 'bar') hasUniqBy: undefined, 60 | 61 | @filter('foo', function(foo, index, array) { return false }) 62 | hasFilter: undefined, 63 | 64 | @map('foo', function(foo, index, array) { return 'bar' }) 65 | hasMap: undefined, 66 | 67 | @sort('foo', function(a, b) { 68 | if (a.priority > b.priority) { 69 | return 1; 70 | } else if (a.priority < b.priority) { 71 | return -1; 72 | } 73 | 74 | return 0; 75 | }) 76 | hasSort: undefined, 77 | 78 | // @glimmer/tracking 79 | 80 | @tracked count: 0, 81 | 82 | // @ember-decorators/component 83 | 84 | @attribute id: '1', 85 | 86 | @className('active', 'inactive') 87 | isActive: true, 88 | 89 | // @ember-decorators/object 90 | 91 | @observes('value') 92 | valueObserver() { 93 | // Executes whenever the "value" property changes 94 | }, 95 | 96 | @on('barEvent') 97 | bar() { 98 | // Executes whenever barEvent is emitted 99 | }, 100 | 101 | @userAdded 102 | yolo() { 103 | // methods always pass through decorators, even if not on allow-list 104 | } 105 | }); 106 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/object-literal-with-decorators.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import { alias } from '@ember/object/computed'; 3 | import EmberObject, { action, set, computed } from '@ember/object'; 4 | import { dependentKeyCompat } from '@ember/object/compat'; 5 | import { tracked } from '@glimmer/tracking'; 6 | import { attribute, className } from '@ember-decorators/component'; 7 | import { observes, on } from '@ember-decorators/object'; 8 | 9 | @classic 10 | class Foo extends EmberObject { 11 | // @ember/object 12 | 13 | @action 14 | toggleShowing() { 15 | set(this, 'isShowing', !this.isShowing); 16 | } 17 | 18 | @computed('firstName', 'lastName') 19 | fullName() { 20 | return `${this.firstName} ${this.lastName}`; 21 | } 22 | 23 | // @ember/object/compat 24 | 25 | @dependentKeyCompat 26 | fullName2() { 27 | return `${this.firstName} ${this.lastName}`; 28 | } 29 | 30 | // @ember/object/computed 31 | 32 | @alias('foo') 33 | hasAlias; 34 | 35 | @and('foo', 'bar') 36 | hasAnd; 37 | 38 | @bool('foo') 39 | hasBool; 40 | 41 | @collect('foo', 'bar') 42 | hasCollect; 43 | 44 | @deprecatingAlias('foo') 45 | hasDeprecatingAlias; 46 | 47 | @empty('foo') 48 | hasEmpty; 49 | 50 | @equal('foo', 'bar') 51 | hasEqual; 52 | 53 | @filterBy('foo', 'bar') 54 | hasFilterBy; 55 | 56 | @gt('foo', 'bar') 57 | hasGt; 58 | 59 | @gte('foo', 'bar') 60 | hasGte; 61 | 62 | @intersect('foo', 'bar') 63 | hasIntersect; 64 | 65 | @lt('foo', 'bar') 66 | hasLt; 67 | 68 | @lte('foo', 'bar') 69 | hasLte; 70 | 71 | @mapBy('foo', 'bar') 72 | hasMapBy; 73 | 74 | @match('foo', /bar/) 75 | hasMatch; 76 | 77 | @max('foo', 'bar') 78 | hasMax; 79 | 80 | @min('foo', 'bar') 81 | hasMin; 82 | 83 | @none('foo') 84 | hasNone; 85 | 86 | @not('foo') 87 | hasNot; 88 | 89 | @notEmpty('foo') 90 | hasNotEmpty; 91 | 92 | @oneWay('foo') 93 | hasOneWay; 94 | 95 | @or('foo', 'bar') 96 | hasOr; 97 | 98 | @readOnly('foo') 99 | hasReadOnly; 100 | 101 | @reads('foo') 102 | hasReads; 103 | 104 | @setDiff('foo', 'bar') 105 | hasSetDiff; 106 | 107 | @sum('foo', 'bar') 108 | hasSum; 109 | 110 | @union('foo', 'bar') 111 | hasUnion; 112 | 113 | @uniq('foo') 114 | hasUniq; 115 | 116 | @uniqBy('foo', 'bar') 117 | hasUniqBy; 118 | 119 | @filter('foo', function(foo, index, array) { return false }) 120 | hasFilter; 121 | 122 | @map('foo', function(foo, index, array) { return 'bar' }) 123 | hasMap; 124 | 125 | @sort('foo', function(a, b) { 126 | if (a.priority > b.priority) { 127 | return 1; 128 | } else if (a.priority < b.priority) { 129 | return -1; 130 | } 131 | 132 | return 0; 133 | }) 134 | hasSort; 135 | 136 | // @glimmer/tracking 137 | 138 | @tracked 139 | count = 0; 140 | 141 | // @ember-decorators/component 142 | 143 | @attribute 144 | id = '1'; 145 | 146 | @className('active', 'inactive') 147 | isActive = true; 148 | 149 | // @ember-decorators/object 150 | 151 | @observes('value') 152 | valueObserver() { 153 | // Executes whenever the "value" property changes 154 | } 155 | 156 | @on('barEvent') 157 | bar() { 158 | // Executes whenever barEvent is emitted 159 | } 160 | 161 | @userAdded 162 | yolo() { 163 | // methods always pass through decorators, even if not on allow-list 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/class-fields-disabled.input.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Program comments 3 | */ 4 | const Foo = Test.extend({ 5 | /** 6 | * Property comments 7 | */ 8 | prop: "defaultValue", 9 | boolProp: true, 10 | numProp: 123, 11 | [MY_VAL]: "val", 12 | 13 | /** 14 | * Method comments 15 | */ 16 | method() { 17 | // do things 18 | }, 19 | 20 | otherMethod: function() {}, 21 | 22 | get accessor() { 23 | return this._value; 24 | }, 25 | 26 | set accessor(value) { 27 | this._value = value; 28 | }, 29 | 30 | anotherMethod() { 31 | this._super(...arguments); 32 | } 33 | }); 34 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/class-fields-disabled.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "classFields": false 3 | } 4 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/class-fields-disabled.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo': 4 | [prop]: Transform not supported - need option '--class-fields=true' 5 | [boolProp]: Transform not supported - need option '--class-fields=true' 6 | [numProp]: Transform not supported - need option '--class-fields=true' 7 | [MY_VAL]: Transform not supported - need option '--class-fields=true' 8 | */ 9 | 10 | /** 11 | * Program comments 12 | */ 13 | const Foo = Test.extend({ 14 | /** 15 | * Property comments 16 | */ 17 | prop: "defaultValue", 18 | boolProp: true, 19 | numProp: 123, 20 | [MY_VAL]: "val", 21 | 22 | /** 23 | * Method comments 24 | */ 25 | method() { 26 | // do things 27 | }, 28 | 29 | otherMethod: function() {}, 30 | 31 | get accessor() { 32 | return this._value; 33 | }, 34 | 35 | set accessor(value) { 36 | this._value = value; 37 | }, 38 | 39 | anotherMethod() { 40 | this._super(...arguments); 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/classic-decorator-disabled.input.js: -------------------------------------------------------------------------------- 1 | const Foo = EmberObject.extend({}); 2 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/classic-decorator-disabled.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "classicDecorator": false 3 | } 4 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/classic-decorator-disabled.output.js: -------------------------------------------------------------------------------- 1 | class Foo extends EmberObject {} 2 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-1.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject, { computed } from "@ember/object"; 2 | 3 | // Do not transform 4 | const Foo1 = EmberObject.extend({ 5 | computed: computed({ 6 | get() {}, 7 | set() {} 8 | }) 9 | }); 10 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-1.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "decorators": false, 3 | "noTelemetry": false 4 | } 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-1.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo1': 4 | [computed]: Transform not supported - need option '--decorators=true' 5 | */ 6 | 7 | import EmberObject, { computed } from "@ember/object"; 8 | 9 | // Do not transform 10 | const Foo1 = EmberObject.extend({ 11 | computed: computed({ 12 | get() {}, 13 | set() {} 14 | }) 15 | }); 16 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-10.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject from "@ember/object"; 2 | import { inject as service } from "@ember/service"; 3 | 4 | // Do not transform 5 | const Foo10 = EmberObject.extend({ 6 | event: service() 7 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-10.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "decorators": false, 3 | "noTelemetry": false 4 | } 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-10.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo10': 4 | [event]: Transform not supported - need option '--decorators=true' 5 | */ 6 | 7 | import EmberObject from "@ember/object"; 8 | import { inject as service } from "@ember/service"; 9 | 10 | // Do not transform 11 | const Foo10 = EmberObject.extend({ 12 | event: service() 13 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-11.input.js: -------------------------------------------------------------------------------- 1 | import { inject as controller } from "@ember/controller"; 2 | import EmberObject from "@ember/object"; 3 | 4 | // Do not transform 5 | const Foo11 = EmberObject.extend({ 6 | event: controller() 7 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-11.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "decorators": false, 3 | "noTelemetry": false 4 | } 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-11.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo11': 4 | [event]: Transform not supported - need option '--decorators=true' 5 | */ 6 | 7 | import { inject as controller } from "@ember/controller"; 8 | import EmberObject from "@ember/object"; 9 | 10 | // Do not transform 11 | const Foo11 = EmberObject.extend({ 12 | event: controller() 13 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-12.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject from "@ember/object"; 2 | 3 | // Do not transform if not a primitive value 4 | const Foo12 = EmberObject.extend({ 5 | macroValue: macro() 6 | }); 7 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-12.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "decorators": false, 3 | "inObjectLiterals": ["macro"], 4 | "noTelemetry": false 5 | } 6 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-12.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo12': 4 | [macroValue]: Transform not supported - need option '--decorators=true' 5 | [macroValue]: Transform not supported - call to 'macro' can not be transformed 6 | */ 7 | 8 | import EmberObject from "@ember/object"; 9 | 10 | // Do not transform if not a primitive value 11 | const Foo12 = EmberObject.extend({ 12 | macroValue: macro() 13 | }); 14 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-13.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject from "@ember/object"; 2 | 3 | // Do not transform 4 | const Foo13 = EmberObject.extend({ 5 | layout: "div" 6 | }); 7 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-13.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "decorators": false, 3 | "noTelemetry": false 4 | } 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-13.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo13': 4 | [layout]: Transform not supported - need option '--decorators=true' 5 | */ 6 | 7 | import EmberObject from "@ember/object"; 8 | 9 | // Do not transform 10 | const Foo13 = EmberObject.extend({ 11 | layout: "div" 12 | }); 13 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-2.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject from "@ember/object"; 2 | 3 | // Do not transform 4 | const Foo2 = EmberObject.extend({ 5 | @tracked prop: '', 6 | }); 7 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-2.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "decorators": false, 3 | "noTelemetry": false 4 | } 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-2.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo2': 4 | [prop]: Transform not supported - need option '--decorators=true' 5 | */ 6 | 7 | import EmberObject from "@ember/object"; 8 | 9 | // Do not transform 10 | const Foo2 = EmberObject.extend({ 11 | @tracked prop: '', 12 | }); 13 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-3.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject from "@ember/object"; 2 | 3 | // Do not transform 4 | const Foo3 = EmberObject.extend({ 5 | tagName: "div" 6 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-3.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "decorators": false, 3 | "noTelemetry": false 4 | } 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-3.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo3': 4 | [tagName]: Transform not supported - need option '--decorators=true' 5 | */ 6 | 7 | import EmberObject from "@ember/object"; 8 | 9 | // Do not transform 10 | const Foo3 = EmberObject.extend({ 11 | tagName: "div" 12 | }); 13 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-4.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject from "@ember/object" 2 | 3 | // Do not transform 4 | const Foo4 = EmberObject.extend({ 5 | classNames: [] 6 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-4.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "decorators": false, 3 | "noTelemetry": false 4 | } 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-4.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo4': 4 | [classNames]: Transform not supported - need option '--decorators=true' 5 | */ 6 | 7 | import EmberObject from "@ember/object" 8 | 9 | // Do not transform 10 | const Foo4 = EmberObject.extend({ 11 | classNames: [] 12 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-5.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject from "@ember/object"; 2 | 3 | // Do not transform 4 | const Foo5 = EmberObject.extend({ 5 | classNameBindings: ["foo"], 6 | foo: "val" 7 | }); 8 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-5.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "decorators": false, 3 | "noTelemetry": false 4 | } 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-5.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo5': 4 | [classNameBindings]: Transform not supported - need option '--decorators=true' 5 | */ 6 | 7 | import EmberObject from "@ember/object"; 8 | 9 | // Do not transform 10 | const Foo5 = EmberObject.extend({ 11 | classNameBindings: ["foo"], 12 | foo: "val" 13 | }); 14 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-6.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject from "@ember/object"; 2 | 3 | // Do not transform 4 | const Foo6 = EmberObject.extend({ 5 | attributeBindings: ["foo"], 6 | foo: "val" 7 | }); 8 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-6.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "decorators": false, 3 | "noTelemetry": false 4 | } 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-6.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo6': 4 | [attributeBindings]: Transform not supported - need option '--decorators=true' 5 | */ 6 | 7 | import EmberObject from "@ember/object"; 8 | 9 | // Do not transform 10 | const Foo6 = EmberObject.extend({ 11 | attributeBindings: ["foo"], 12 | foo: "val" 13 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-7.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject from "@ember/object"; 2 | 3 | // Do not transform 4 | const Foo7 = EmberObject.extend({ 5 | actions: { 6 | bar() {} 7 | } 8 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-7.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "decorators": false, 3 | "noTelemetry": false 4 | } 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-7.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo7': 4 | [actions]: Transform not supported - need option '--decorators=true' 5 | */ 6 | 7 | import EmberObject from "@ember/object"; 8 | 9 | // Do not transform 10 | const Foo7 = EmberObject.extend({ 11 | actions: { 12 | bar() {} 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-8.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject,{ observer } from "@ember/object"; 2 | 3 | // Do not transform 4 | const Foo8 = EmberObject.extend({ 5 | observer: observer() 6 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-8.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "decorators": false, 3 | "noTelemetry": false 4 | } 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-8.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo8': 4 | [observer]: Transform not supported - need option '--decorators=true' 5 | */ 6 | 7 | import EmberObject,{ observer } from "@ember/object"; 8 | 9 | // Do not transform 10 | const Foo8 = EmberObject.extend({ 11 | observer: observer() 12 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-9.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject from "@ember/object"; 2 | import { on } from "@ember/object/evented"; 3 | 4 | // Do not transform 5 | const Foo9 = EmberObject.extend({ 6 | event: on() 7 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-9.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "decorators": false, 3 | "noTelemetry": false 4 | } 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/decorators-disabled-9.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo9': 4 | [event]: Transform not supported - need option '--decorators=true' 5 | */ 6 | 7 | import EmberObject from "@ember/object"; 8 | import { on } from "@ember/object/evented"; 9 | 10 | // Do not transform 11 | const Foo9 = EmberObject.extend({ 12 | event: on() 13 | }); -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/ignore-leaking-state.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject from '@ember/object'; 2 | 3 | const Foo = EmberObject.extend({ 4 | queryParams: {}, 5 | ignoreMyLeakyState: {} 6 | }); 7 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/ignore-leaking-state.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignoreLeakingState": ["queryParams", "ignoreMyLeakyState"] 3 | } 4 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/ignore-leaking-state.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import EmberObject from '@ember/object'; 3 | 4 | @classic 5 | class Foo extends EmberObject { 6 | queryParams = {}; 7 | ignoreMyLeakyState = {}; 8 | } -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/object-literal-decorators.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject from '@ember/object'; 2 | 3 | const Foo = EmberObject.extend({ 4 | @userAdded 5 | whatever: undefined 6 | }); 7 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/object-literal-decorators.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "decorators": { 3 | "inObjectLiterals": ["userAdded"] 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/object-literal-decorators.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import EmberObject from '@ember/object'; 3 | 4 | @classic 5 | class Foo extends EmberObject { 6 | @userAdded 7 | whatever; 8 | } -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/query-params-disallowed.input.js: -------------------------------------------------------------------------------- 1 | import EmberObject from '@ember/object'; 2 | 3 | // Should not transform 4 | const Foo = EmberObject.extend({ 5 | queryParams: {} 6 | }); 7 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/query-params-disallowed.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignoreLeakingState": [] 3 | } 4 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/query-params-disallowed.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo': 4 | [queryParams]: Transform not supported - value is of type object. For more details: eslint-plugin-ember/avoid-leaking-state-in-ember-objects 5 | */ 6 | 7 | import EmberObject from '@ember/object'; 8 | 9 | // Should not transform 10 | const Foo = EmberObject.extend({ 11 | queryParams: {} 12 | }); 13 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/string-boolean.input.js: -------------------------------------------------------------------------------- 1 | const Foo1 = EmberObject.extend({}); 2 | 3 | /** 4 | * Program comments 5 | */ 6 | const Foo2 = Test.extend({ 7 | /** 8 | * Property comments 9 | */ 10 | prop: "defaultValue", 11 | boolProp: true, 12 | numProp: 123, 13 | [MY_VAL]: "val", 14 | 15 | /** 16 | * Method comments 17 | */ 18 | method() { 19 | // do things 20 | }, 21 | 22 | otherMethod: function() {}, 23 | 24 | get accessor() { 25 | return this._value; 26 | }, 27 | 28 | set accessor(value) { 29 | this._value = value; 30 | }, 31 | 32 | anotherMethod() { 33 | this._super(...arguments); 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/string-boolean.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "classicDecorator": "false", 3 | "classFields": "false", 4 | "decorators": "true" 5 | } 6 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/options/string-boolean.output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Expect error: 3 | ValidationError: Validation errors for class 'Foo2': 4 | [prop]: Transform not supported - need option '--class-fields=true' 5 | [boolProp]: Transform not supported - need option '--class-fields=true' 6 | [numProp]: Transform not supported - need option '--class-fields=true' 7 | [MY_VAL]: Transform not supported - need option '--class-fields=true' 8 | */ 9 | 10 | const Foo1 = EmberObject.extend({}); 11 | 12 | /** 13 | * Program comments 14 | */ 15 | const Foo2 = Test.extend({ 16 | /** 17 | * Property comments 18 | */ 19 | prop: "defaultValue", 20 | boolProp: true, 21 | numProp: 123, 22 | [MY_VAL]: "val", 23 | 24 | /** 25 | * Method comments 26 | */ 27 | method() { 28 | // do things 29 | }, 30 | 31 | otherMethod: function() {}, 32 | 33 | get accessor() { 34 | return this._value; 35 | }, 36 | 37 | set accessor(value) { 38 | this._value = value; 39 | }, 40 | 41 | anotherMethod() { 42 | this._super(...arguments); 43 | } 44 | }); 45 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/runtime.input.js: -------------------------------------------------------------------------------- 1 | import { computed, observer } from '@ember/object'; 2 | import { alias } from '@ember/object/computed'; 3 | import { service } from '@ember/service'; 4 | import Runtime from 'common/runtime'; 5 | import { customMacro, customMacroWithInput } from 'my-app/lib'; 6 | 7 | /** 8 | * Program comments 9 | */ 10 | export default Runtime.extend(MyMixin, { 11 | /** 12 | * Property comments 13 | */ 14 | prop: 'defaultValue', 15 | boolProp: true, 16 | numProp: 123, 17 | [MY_VAL]: 'val', 18 | queryParams: {}, 19 | 20 | error: service(), 21 | errorService: service('error'), 22 | 23 | observerProp: observer('prop', function() { return this.prop; }), 24 | 25 | unobservedProp: null, 26 | offProp: null, 27 | 28 | numPlusOne: computed('numProp', function() { 29 | return this.get('numProp') + 1; 30 | }), 31 | 32 | numPlusPlus: alias('numPlusOne'), 33 | 34 | computedMacro: customMacro(), 35 | 36 | anotherMacro: customMacroWithInput({ 37 | foo: 123, 38 | bar: 'baz' 39 | }), 40 | 41 | /** 42 | * Method comments 43 | */ 44 | method() { 45 | // do things 46 | }, 47 | 48 | otherMethod: function() {}, 49 | 50 | get accessor() { 51 | return this._value; 52 | }, 53 | 54 | set accessor(value) { 55 | this._value = value; 56 | }, 57 | 58 | anotherMethod() { 59 | this._super(...arguments); 60 | }, 61 | 62 | overriddenMethod() { 63 | this._super(...arguments); 64 | }, 65 | 66 | actions: { 67 | actionMethod() { 68 | this._super(...arguments) && this.boolProp; 69 | }, 70 | 71 | overriddenActionMethod() { 72 | this._super(...arguments) && this.boolProp; 73 | }, 74 | }, 75 | }); 76 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/runtime.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "noTelemetry": "false" 3 | } -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/runtime.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import { off, unobserves, observes } from '@ember-decorators/object'; 3 | import { action, computed } from '@ember/object'; 4 | import { service } from '@ember/service'; 5 | import { alias } from '@ember/object/computed'; 6 | import Runtime from 'common/runtime'; 7 | import { customMacro, customMacroWithInput } from 'my-app/lib'; 8 | 9 | /** 10 | * Program comments 11 | */ 12 | @classic 13 | export default class _Runtime extends Runtime.extend(MyMixin) { 14 | /** 15 | * Property comments 16 | */ 17 | prop = 'defaultValue'; 18 | 19 | boolProp = true; 20 | numProp = 123; 21 | [MY_VAL] = 'val'; 22 | queryParams = {}; 23 | 24 | @service 25 | error; 26 | 27 | @service('error') 28 | errorService; 29 | 30 | @observes('prop') 31 | observerProp() { return this.prop; } 32 | 33 | @unobserves('prop3', 'prop4') 34 | unobservedProp; 35 | 36 | @off('prop1', 'prop2') 37 | offProp; 38 | 39 | @computed('numProp') 40 | get numPlusOne() { 41 | return this.get('numProp') + 1; 42 | } 43 | 44 | @alias('numPlusOne') 45 | numPlusPlus; 46 | 47 | @customMacro() 48 | computedMacro; 49 | 50 | @customMacroWithInput({ 51 | foo: 123, 52 | bar: 'baz' 53 | }) 54 | anotherMacro; 55 | 56 | /** 57 | * Method comments 58 | */ 59 | method() { 60 | // do things 61 | } 62 | 63 | otherMethod() {} 64 | 65 | get accessor() { 66 | return this._value; 67 | } 68 | 69 | set accessor(value) { 70 | this._value = value; 71 | } 72 | 73 | anotherMethod() { 74 | undefined; 75 | } 76 | 77 | overriddenMethod() { 78 | super.overriddenMethod(...arguments); 79 | } 80 | 81 | @action 82 | actionMethod() { 83 | undefined && this.boolProp; 84 | } 85 | 86 | @action 87 | overriddenActionMethod() { 88 | // TODO: This call to super is within an action, and has to refer to the parent 89 | // class's actions to be safe. This should be refactored to call a normal method 90 | // on the parent class. If the parent class has not been converted to native 91 | // classes, it may need to be refactored as well. See 92 | // https://github.com/scalvert/ember-native-class-codemod/blob/master/README.md 93 | // for more details. 94 | super.actions.overriddenActionMethod.call(this, ...arguments) && this.boolProp; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/service-basic.input.js: -------------------------------------------------------------------------------- 1 | import { service } from '@ember/service'; 2 | 3 | const Foo1 = EmberObject.extend({ 4 | b: service('store'), 5 | }); 6 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/service-basic.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import { service } from '@ember/service'; 3 | 4 | @classic 5 | class Foo1 extends EmberObject { 6 | @service('store') 7 | b; 8 | } 9 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/components/basic.input.js: -------------------------------------------------------------------------------- 1 | import Component from '@ember/component'; 2 | 3 | export default Component.extend({}); 4 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/components/basic.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import Component from '@ember/component'; 3 | 4 | @classic 5 | export default class Basic extends Component {} 6 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/controllers/basic.input.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | 3 | export default Controller.extend({}); 4 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/controllers/basic.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import Controller from '@ember/controller'; 3 | 4 | @classic 5 | export default class BasicController extends Controller {} 6 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/helpers/basic.input.js: -------------------------------------------------------------------------------- 1 | import Helper from '@ember/component/helper'; 2 | 3 | export default Helper.extend({}); 4 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/helpers/basic.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import Helper from '@ember/component/helper'; 3 | 4 | @classic 5 | export default class Basic extends Helper {} 6 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/invalid/basic.input.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Program comments 3 | */ 4 | const Foo = Test.extend({}); 5 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/invalid/basic.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "components" 3 | } 4 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/invalid/basic.output.js: -------------------------------------------------------------------------------- 1 | /* Expect skipped */ 2 | 3 | /** 4 | * Program comments 5 | */ 6 | const Foo = Test.extend({}); 7 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/pods/foo/controller.input.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | 3 | export default Controller.extend({}); 4 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/pods/foo/controller.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import Controller from '@ember/controller'; 3 | 4 | @classic 5 | export default class FooController extends Controller {} 6 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/routes/basic.input.js: -------------------------------------------------------------------------------- 1 | import Route from '@ember/route'; 2 | 3 | export default Route.extend({}); 4 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/routes/basic.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import Route from '@ember/route'; 3 | 4 | @classic 5 | export default class BasicRoute extends Route {} 6 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/services/basic.input.js: -------------------------------------------------------------------------------- 1 | import Service from '@ember/service'; 2 | 3 | /** 4 | * Program comments 5 | */ 6 | export default Service.extend({}); 7 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/services/basic.options.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "services" 3 | } 4 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/services/basic.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import Service from '@ember/service'; 3 | 4 | /** 5 | * Program comments 6 | */ 7 | @classic 8 | export default class BasicService extends Service {} 9 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/services/some-service.input.js: -------------------------------------------------------------------------------- 1 | import Service from '@ember/service'; 2 | 3 | export default Service.extend(); 4 | -------------------------------------------------------------------------------- /transforms/ember-object/__testfixtures__/types/services/some-service.output.js: -------------------------------------------------------------------------------- 1 | import classic from 'ember-classic-decorator'; 2 | import Service from '@ember/service'; 3 | 4 | @classic 5 | export default class SomeService extends Service {} 6 | -------------------------------------------------------------------------------- /transforms/ember-object/index.ts: -------------------------------------------------------------------------------- 1 | import type { Transform } from 'jscodeshift'; 2 | import { inspect } from 'node:util'; 3 | import path from 'path'; 4 | import type * as AST from '../helpers/ast'; 5 | import getConfig from '../helpers/config'; 6 | import logger from '../helpers/log-helper'; 7 | import maybeTransformEmberObjects from '../helpers/transform'; 8 | import { isFileOfType, isTestFile } from '../helpers/validation-helper'; 9 | 10 | /** 11 | * | Result | How-to | Meaning | 12 | * | :------ | :------ | :------- | 13 | * | `errors` | `throw` | we attempted to transform but encountered an error | 14 | * | `unmodified` | return `string` (unchanged) | we attempted to transform but it was unnecessary | 15 | * | `skipped` | return `undefined` | we did not attempt to transform | 16 | * | `ok` | return `string` (changed) | we successfully transformed | 17 | */ 18 | const transformer: Transform = function ( 19 | { source, path: filePath }, 20 | { jscodeshift: j } 21 | ) { 22 | try { 23 | const extension = path.extname(filePath); 24 | if (!['.js', '.ts'].includes(extension.toLowerCase())) { 25 | // do nothing on non-js/ts files 26 | return; // status: 'skipped' 27 | } 28 | 29 | if (isTestFile(filePath)) { 30 | logger.debug({ filePath, info: 'SKIPPED: test file' }); 31 | return; // status: 'skipped' 32 | } 33 | 34 | const userOptions = getConfig(); 35 | 36 | if (userOptions.type && !isFileOfType(filePath, userOptions.type)) { 37 | logger.info({ 38 | filePath, 39 | info: `SKIPPED: Type mismatch, expected type '${userOptions.type}' did not match type of file`, 40 | }); 41 | return; // status: 'skipped' 42 | } 43 | 44 | const root = j(source) as AST.Collection; 45 | return maybeTransformEmberObjects(root, filePath, userOptions) // might throw --> status: 'errors' 46 | ? root.toSource({ quote: userOptions.quote }) // status: `ok` 47 | : source; // status: `unchanged` 48 | } catch (rawError: unknown) { 49 | const error = 50 | rawError instanceof Error 51 | ? rawError 52 | : new Error(`Unknown Error: ${inspect(rawError)}`); 53 | logger.error({ filePath, error }); 54 | throw error; // status: `errors` 55 | } 56 | }; 57 | 58 | export default transformer; 59 | 60 | // Set the parser, needed for supporting decorators 61 | export { default as parser } from '../helpers/parse'; 62 | -------------------------------------------------------------------------------- /transforms/helpers/config.ts: -------------------------------------------------------------------------------- 1 | import { getOptions } from 'codemod-cli'; 2 | import { cosmiconfigSync } from 'cosmiconfig'; 3 | import { deepmergeCustom } from 'deepmerge-ts'; 4 | import type { PartialUserOptions, UserOptions } from './options'; 5 | import { DEFAULT_OPTIONS, UserOptionsSchema, parseConfig } from './options'; 6 | 7 | export const mergeConfig = deepmergeCustom({ 8 | mergeArrays: (values, _utils, _meta) => { 9 | return values[values.length - 1]; // overwrite with the last provided array 10 | }, 11 | }); 12 | 13 | /** 14 | * Returns a UserOptions object merging options from three sources: 15 | * - DEFAULT_OPTIONS 16 | * - a config file (which overrides the above) 17 | * - ENV variables (which overrides the above) 18 | */ 19 | export default function getConfig(dir = process.cwd()): UserOptions { 20 | const config = mergeConfig( 21 | DEFAULT_OPTIONS, 22 | getFileConfig(dir), 23 | getCliConfig() 24 | ); 25 | if (process.env['NO_TELEMETRY'] === 'true') { 26 | config.noTelemetry = true; 27 | } 28 | return UserOptionsSchema.parse(config); 29 | } 30 | 31 | const searchPlaces = [ 32 | 'package.json', 33 | '.codemods.json', 34 | '.codemods.js', 35 | '.codemods.cjs', 36 | '.codemods.yaml', 37 | '.codemods.yml', 38 | ]; 39 | 40 | function getFileConfig(dir: string): PartialUserOptions { 41 | const explorer = cosmiconfigSync('codemods', { searchPlaces }); 42 | const result = explorer.search(dir); 43 | return result ? parseConfig(result.filepath, result.config) : {}; 44 | } 45 | 46 | function getCliConfig(): PartialUserOptions { 47 | return parseConfig('CLI', getOptions()); 48 | } 49 | -------------------------------------------------------------------------------- /transforms/helpers/decorator-helper.ts: -------------------------------------------------------------------------------- 1 | import type { JSCodeshift } from 'jscodeshift'; 2 | import { default as j } from 'jscodeshift'; 3 | import type * as AST from '../helpers/ast'; 4 | 5 | type CallExpressionArg = Parameters[1][number]; 6 | 7 | /** Creates a decorator for a class. */ 8 | export function createClassDecorator( 9 | decoratorName: string, 10 | value: CallExpressionArg 11 | ): AST.Decorator { 12 | const args = value.type === 'ArrayExpression' ? value.elements : [value]; 13 | return j.decorator( 14 | j.callExpression(j.identifier(decoratorName), args as CallExpressionArg[]) 15 | ); 16 | } 17 | 18 | /** Create decorators which need arguments. */ 19 | export function createDecoratorWithArgs( 20 | decoratorName: string, 21 | args: Array 22 | ): AST.Decorator { 23 | return j.decorator( 24 | j.callExpression( 25 | j.identifier(decoratorName), 26 | args.map((arg) => j.literal(arg)) 27 | ) 28 | ); 29 | } 30 | 31 | /** Create simple decorator with given name. */ 32 | export function createIdentifierDecorator( 33 | decoratorName: string 34 | ): AST.Decorator { 35 | return j.decorator(j.identifier(decoratorName)); 36 | } 37 | -------------------------------------------------------------------------------- /transforms/helpers/decorator-info.ts: -------------------------------------------------------------------------------- 1 | import type * as AST from '../helpers/ast'; 2 | import { COMPUTED_DECORATOR_NAME, METHOD_DECORATORS } from './util/index'; 3 | import { assert } from './util/types'; 4 | 5 | export interface DecoratorImportInfo { 6 | name: string; 7 | importedName?: string; 8 | isImportedAs?: boolean; 9 | isComputedDecorator?: boolean; 10 | isMetaDecorator?: boolean; 11 | isMethodDecorator?: boolean; 12 | localName?: string; 13 | args?: Array; 14 | } 15 | 16 | export type DecoratorImportInfoMap = Map< 17 | /** local name */ string, 18 | DecoratorImportInfo 19 | >; 20 | 21 | /** 22 | * Return the decorator name for the specifier if any, using the importPropDecoratorMap from 23 | * `DECORATOR_PATHS` config (defined util.js) 24 | */ 25 | export function getDecoratorImportInfo( 26 | specifier: AST.ImportSpecifier, 27 | importPropDecoratorMap: Record | undefined 28 | ): DecoratorImportInfo { 29 | const localName = specifier.local?.name; 30 | const importedName = specifier.imported.name; 31 | const isImportedAs = importedName !== localName; 32 | const isMetaDecorator = !importPropDecoratorMap; 33 | let name: string; 34 | if (isImportedAs || isMetaDecorator) { 35 | assert(localName, 'expected local name'); 36 | name = localName; 37 | } else { 38 | const newName = importPropDecoratorMap[importedName]; 39 | assert(newName, 'expected importPropDecoratorMap[importedName]'); 40 | name = newName; 41 | } 42 | 43 | const isMethodDecorator = METHOD_DECORATORS.has(importedName); 44 | const isComputedDecorator = COMPUTED_DECORATOR_NAME === importedName; 45 | return { 46 | name, 47 | importedName, 48 | isImportedAs, 49 | isComputedDecorator, 50 | isMetaDecorator, 51 | isMethodDecorator, 52 | localName, 53 | }; 54 | } 55 | -------------------------------------------------------------------------------- /transforms/helpers/eo-prop/index.ts: -------------------------------------------------------------------------------- 1 | import * as AST from '../ast'; 2 | import type { DecoratorImportInfoMap } from '../decorator-info'; 3 | import type { Options } from '../options'; 4 | import { assert } from '../util/types'; 5 | import EOActionsProp from './private/actions'; 6 | import EOClassDecorator from './private/class-decorator'; 7 | import type EOComputedFunctionExpressionGetter from './private/computed/function-expression-getter'; 8 | import type EOComputedFunctionExpressionMethod from './private/computed/function-expression-method'; 9 | import { makeEOComputedProp } from './private/computed/index'; 10 | import type EOComputedObjectExpressionProp from './private/computed/object-expression'; 11 | import type EOComputedProp from './private/computed/property'; 12 | import EOFunctionExpressionProp from './private/function-expression'; 13 | import EOMethod from './private/method'; 14 | import EOSimpleProp from './private/simple'; 15 | 16 | // Intentionally not included in EOProp union type. 17 | export type { default as EOClassDecorator } from './private/class-decorator'; 18 | 19 | export type EOProp = 20 | | EOActionsProp 21 | | EOSimpleProp 22 | | EOComputedFunctionExpressionGetter 23 | | EOComputedFunctionExpressionMethod 24 | | EOComputedObjectExpressionProp 25 | | EOComputedProp 26 | | EOFunctionExpressionProp 27 | | EOMethod; 28 | 29 | /** 30 | * Makes an object representing an Ember Object property. 31 | */ 32 | export default function makeEOProp( 33 | eoProp: AST.EOExpressionProp, 34 | existingDecoratorImportInfos: DecoratorImportInfoMap, 35 | options: Options 36 | ): EOProp | EOClassDecorator { 37 | if (AST.isEOCallExpressionProp(eoProp)) { 38 | return makeEOComputedProp(eoProp, existingDecoratorImportInfos, options); 39 | } else if (AST.isEOMethod(eoProp)) { 40 | return new EOMethod(eoProp, options); 41 | } else if (AST.isEOFunctionExpressionProp(eoProp)) { 42 | return new EOFunctionExpressionProp(eoProp, options); 43 | } else if (AST.isEOClassDecoratorProp(eoProp)) { 44 | return new EOClassDecorator(eoProp, options); 45 | } else if (AST.isEOActionsProp(eoProp)) { 46 | return new EOActionsProp(eoProp, options); 47 | } else { 48 | assert(AST.isEOSimpleProp(eoProp)); 49 | return new EOSimpleProp(eoProp, options); 50 | } 51 | } 52 | 53 | /** Type predicate */ 54 | export function isEOProp(p: EOProp | EOClassDecorator): p is EOProp { 55 | return !p.isClassDecorator; 56 | } 57 | 58 | /** Type predicate */ 59 | export function isEOClassDecorator( 60 | p: EOProp | EOClassDecorator 61 | ): p is EOClassDecorator { 62 | return p.isClassDecorator; 63 | } 64 | -------------------------------------------------------------------------------- /transforms/helpers/eo-prop/private/actions/index.ts: -------------------------------------------------------------------------------- 1 | import * as AST from '../../../ast'; 2 | import type { DecoratorImportSpecs } from '../../../util/index'; 3 | import { LIFECYCLE_HOOKS } from '../../../util/index'; 4 | import AbstractEOProp from '../abstract'; 5 | import EOActionMethod from './method'; 6 | import EOActionProp from './property'; 7 | 8 | export interface Action { 9 | hasInfiniteLoop: boolean; 10 | } 11 | 12 | /** 13 | * Ember Object Actions Property 14 | * 15 | * A wrapper object for Ember Object `actions` object properties to be 16 | * transformed into a series of `ClassMethod`s with the `@action` decorator. 17 | * 18 | * Each action on the object is represented either by an `EOActionMethod` or 19 | * `EOActionProp`. 20 | * 21 | * @example 22 | * 23 | * ``` 24 | * import someActionUtil from 'some/action/util'; 25 | * 26 | * const MyObject = EmberObject.extend({ 27 | * actions: { 28 | * someActionUtil, 29 | * bar() {}, 30 | * } 31 | * }); 32 | * ``` 33 | * 34 | * transforms into: 35 | * 36 | * ``` 37 | * import someActionUtil from 'some/action/util'; 38 | * 39 | * class MyObject extends EmberObject { 40 | * @action 41 | * someActionUtil() { 42 | * return someActionUtil.call(this, ...arguments); 43 | * } 44 | * 45 | * @action 46 | * bar() {} 47 | * } 48 | * ``` 49 | */ 50 | export default class EOActionsProp extends AbstractEOProp< 51 | AST.EOActionsProp, 52 | AST.ClassMethod[] 53 | > { 54 | readonly isClassDecorator = false as const; 55 | 56 | protected readonly value = this.rawProp.value; 57 | 58 | override get decoratorImportSpecs(): DecoratorImportSpecs { 59 | return { 60 | ...super.decoratorImportSpecs, 61 | action: true, 62 | }; 63 | } 64 | 65 | build(): AST.ClassMethod[] { 66 | return this.actions.map((action) => { 67 | return action.build(); 68 | }); 69 | } 70 | 71 | // eslint-disable-next-line @typescript-eslint/class-literal-property-style 72 | protected override get needsDecorators(): boolean { 73 | return true; 74 | } 75 | 76 | protected override get typeErrors(): string[] { 77 | return [...this.lifecycleHookErrors, ...this.infiniteLoopErrors]; 78 | } 79 | 80 | private get actions(): Array { 81 | return this.value.properties.map((raw) => 82 | AST.isEOActionMethod(raw) 83 | ? new EOActionMethod(raw, this.options) 84 | : new EOActionProp(raw, this.options) 85 | ); 86 | } 87 | 88 | /** 89 | * Iterate over actions and verify that the action name does not match the lifecycle hooks 90 | * The transformation is not supported if an action has the same name as lifecycle hook 91 | * Reference: https://github.com/scalvert/ember-native-class-codemod/issues/34 92 | */ 93 | private get lifecycleHookErrors(): string[] { 94 | const { actions } = this; 95 | const errors: string[] = []; 96 | for (const action of actions) { 97 | const { name } = action; 98 | if (LIFECYCLE_HOOKS.has(name)) { 99 | errors.push( 100 | this.makeActionError( 101 | name, 102 | 'action name matches one of the lifecycle hooks. Rename and try again. See https://github.com/scalvert/ember-native-class-codemod/issues/34 for more details' 103 | ) 104 | ); 105 | } 106 | } 107 | return errors; 108 | } 109 | 110 | /** 111 | * Validation against pattern mentioned https://github.com/scalvert/eslint-plugin-ember-es6-class/pull/2 112 | */ 113 | private get infiniteLoopErrors(): string[] { 114 | const { actions } = this; 115 | const errors: string[] = []; 116 | for (const action of actions) { 117 | if (action.hasInfiniteLoop) { 118 | const { name } = action; 119 | errors.push( 120 | this.makeActionError( 121 | name, 122 | `calling the passed action would cause an infinite loop. See https://github.com/scalvert/eslint-plugin-ember-es6-class/pull/2 for more details` 123 | ) 124 | ); 125 | } 126 | 127 | return errors; 128 | } 129 | return errors; 130 | } 131 | 132 | private makeActionError(actionName: string, message: string): string { 133 | return this.makeError(`[${actionName}]: ${message}`); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /transforms/helpers/eo-prop/private/actions/method.ts: -------------------------------------------------------------------------------- 1 | import { default as j } from 'jscodeshift'; 2 | import * as AST from '../../../ast'; 3 | import { createIdentifierDecorator } from '../../../decorator-helper'; 4 | import { replaceActionSuperExpressions } from '../../../transform-helper'; 5 | import { ACTION_DECORATOR_NAME } from '../../../util/index'; 6 | import EOMethod from '../method'; 7 | import type { Action } from './index'; 8 | 9 | /** 10 | * Ember Object Action Method 11 | * 12 | * A wrapper object for a method property of an Ember Object `actions` object 13 | * property. 14 | * 15 | * It will be transformed into a `ClassMethod` with the `@action` decorator. 16 | * 17 | * @example 18 | * 19 | * ``` 20 | * const MyObject = EmberObject.extend({ 21 | * actions: { 22 | * bar() {}, 23 | * baz() { 24 | * this._super(...arguments); 25 | * } 26 | * } 27 | * }); 28 | * ``` 29 | * 30 | * transforms into: 31 | * 32 | * ``` 33 | * class MyObject extends EmberObject { 34 | * @action 35 | * bar() {} 36 | * 37 | * @action 38 | * baz() { 39 | * // TODO: This call to super is within an action, and has to refer to the parent 40 | * // class's actions to be safe. This should be refactored to call a normal method 41 | * // on the parent class. If the parent class has not been converted to native 42 | * // classes, it may need to be refactored as well. See 43 | * // https://github.com/scalvert/ember-native-class-codemod/blob/master/README.md 44 | * // for more details. 45 | * super.actions.baz.call(this, ...arguments); 46 | * } 47 | * } 48 | * ``` 49 | */ 50 | export default class EOActionMethod extends EOMethod implements Action { 51 | get hasInfiniteLoop(): boolean { 52 | const { name, value } = this; 53 | const collection = j(value.body) as AST.Collection; 54 | 55 | // Occurrences of this.actionName() 56 | const isEOActionInfiniteCall = AST.makeEOActionInfiniteCallPredicate(name); 57 | const actionCalls = AST.findPaths( 58 | collection, 59 | j.CallExpression, 60 | isEOActionInfiniteCall 61 | ); 62 | 63 | // Occurrences of this.get('actionName')() or get(this, 'actionName')() 64 | const isEOActionInfiniteLiteral = 65 | AST.makeEOActionInfiniteLiteralPredicate(name); 66 | const actionLiterals = AST.findPaths( 67 | collection, 68 | j.StringLiteral, 69 | isEOActionInfiniteLiteral 70 | ); 71 | 72 | return actionLiterals.length > 0 || actionCalls.length > 0; 73 | } 74 | 75 | override build(): AST.ClassMethod { 76 | return replaceActionSuperExpressions( 77 | j.classMethod.from({ 78 | kind: this.kind, 79 | key: this.key, 80 | params: this.params, 81 | body: this.body, 82 | comments: this.comments, 83 | decorators: [createIdentifierDecorator(ACTION_DECORATOR_NAME)], 84 | }), 85 | this.replaceSuperWithUndefined 86 | ); 87 | } 88 | 89 | protected override get isOverridden(): boolean { 90 | return this.runtimeData?.overriddenActions.includes(this.name) ?? false; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /transforms/helpers/eo-prop/private/actions/property.ts: -------------------------------------------------------------------------------- 1 | import { default as j } from 'jscodeshift'; 2 | import type * as AST from '../../../ast'; 3 | import { createIdentifierDecorator } from '../../../decorator-helper'; 4 | import { ACTION_DECORATOR_NAME } from '../../../util/index'; 5 | import AbstractEOProp from '../abstract'; 6 | import type { Action } from './index'; 7 | 8 | /** 9 | * Ember Object Action Property 10 | * 11 | * A wrapper object for an Identifier property of an Ember Object `actions` 12 | * object property. 13 | * 14 | * It will be transformed into a `ClassMethod` with the `@action` decorator. 15 | * 16 | * @example 17 | * 18 | * ``` 19 | * import someActionUtil from 'some/action/util'; 20 | * 21 | * const MyObject = EmberObject.extend({ 22 | * actions: { 23 | * someActionUtil 24 | * } 25 | * }); 26 | * ``` 27 | * 28 | * transforms into: 29 | * 30 | * ``` 31 | * import someActionUtil from 'some/action/util'; 32 | * 33 | * class MyObject extends EmberObject { 34 | * @action 35 | * someActionUtil() { 36 | * return someActionUtil.call(this, ...arguments); 37 | * } 38 | * } 39 | * ``` 40 | */ 41 | export default class EOActionProp 42 | extends AbstractEOProp 43 | implements Action 44 | { 45 | readonly isClassDecorator = false as const; 46 | 47 | readonly hasInfiniteLoop = false; 48 | 49 | protected readonly value = this.rawProp.value; 50 | 51 | build(): AST.ClassMethod { 52 | const body = j.blockStatement([ 53 | j.returnStatement( 54 | j.callExpression(j.memberExpression(this.value, j.identifier('call')), [ 55 | j.thisExpression(), 56 | j.spreadElement(j.identifier('arguments')), 57 | ]) 58 | ), 59 | ]); 60 | 61 | return j.classMethod.from({ 62 | kind: 'method', 63 | key: this.key, 64 | params: [], 65 | body, 66 | comments: this.comments, 67 | decorators: [createIdentifierDecorator(ACTION_DECORATOR_NAME)], 68 | }); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /transforms/helpers/eo-prop/private/class-decorator.ts: -------------------------------------------------------------------------------- 1 | import type * as AST from '../../ast'; 2 | import { createClassDecorator } from '../../decorator-helper'; 3 | import type { DecoratorImportSpecs } from '../../util/index'; 4 | import { 5 | ATTRIBUTE_BINDINGS_DECORATOR_NAME, 6 | CLASS_NAMES_DECORATOR_NAME, 7 | CLASS_NAME_BINDINGS_DECORATOR_NAME, 8 | LAYOUT_DECORATOR_LOCAL_NAME, 9 | LAYOUT_DECORATOR_NAME, 10 | TAG_NAME_DECORATOR_NAME, 11 | } from '../../util/index'; 12 | import AbstractEOProp from './abstract'; 13 | 14 | /** 15 | * Ember Object Class Decorator 16 | * 17 | * A wrapper object for Ember Object properties that should be converted into 18 | * class decorators. See `CLASS_DECORATOR_NAMES` for the list of handled 19 | * decorator properties. 20 | * 21 | * @example 22 | * 23 | * ``` 24 | * const MyObject = EmberObject.extend({ 25 | * tagName: '', 26 | * classNames: ['my-object'], 27 | * }); 28 | * ``` 29 | * 30 | * transforms into: 31 | * 32 | * ``` 33 | * @tagName('') 34 | * @classNames('my-object') 35 | * class MyObject extends EmberObject { 36 | * } 37 | */ 38 | export default class EOClassDecorator extends AbstractEOProp< 39 | AST.EOClassDecoratorProp, 40 | AST.Decorator 41 | > { 42 | readonly isClassDecorator = true as const; 43 | 44 | protected readonly value = this.rawProp.value; 45 | 46 | override get decoratorImportSpecs(): DecoratorImportSpecs { 47 | return { 48 | ...super.decoratorImportSpecs, 49 | classNames: this.isClassNames, 50 | classNameBindings: this.isClassNameBindings, 51 | attributeBindings: this.isAttributeBindings, 52 | layout: this.isLayoutDecorator, 53 | templateLayout: this.isTemplateLayoutDecorator, 54 | tagName: this.isTagName, 55 | }; 56 | } 57 | 58 | build(): AST.Decorator { 59 | return createClassDecorator(this.classDecoratorName, this.value); 60 | } 61 | 62 | // eslint-disable-next-line @typescript-eslint/class-literal-property-style 63 | protected override get needsDecorators(): true { 64 | return true; 65 | } 66 | 67 | private get isLayoutDecorator(): boolean { 68 | return this.classDecoratorName === LAYOUT_DECORATOR_NAME; 69 | } 70 | 71 | private get isTemplateLayoutDecorator(): boolean { 72 | return this.classDecoratorName === LAYOUT_DECORATOR_LOCAL_NAME; 73 | } 74 | 75 | private get isTagName(): boolean { 76 | return this.name === TAG_NAME_DECORATOR_NAME; 77 | } 78 | 79 | private get isClassNames(): boolean { 80 | return this.name === CLASS_NAMES_DECORATOR_NAME; 81 | } 82 | 83 | private get isClassNameBindings(): boolean { 84 | return this.name === CLASS_NAME_BINDINGS_DECORATOR_NAME; 85 | } 86 | 87 | private get isAttributeBindings(): boolean { 88 | return this.name === ATTRIBUTE_BINDINGS_DECORATOR_NAME; 89 | } 90 | 91 | private get classDecoratorName(): string { 92 | if ( 93 | this.name === LAYOUT_DECORATOR_NAME && 94 | 'name' in this.value && // e.g. CallExpression doesn't have `name` 95 | this.value.name === LAYOUT_DECORATOR_NAME 96 | ) { 97 | return LAYOUT_DECORATOR_LOCAL_NAME; 98 | } 99 | return this.name; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /transforms/helpers/eo-prop/private/computed/function-expression-getter.ts: -------------------------------------------------------------------------------- 1 | import { default as j } from 'jscodeshift'; 2 | import type * as AST from '../../../ast'; 3 | import { replaceGetterSetterSuperExpressions } from '../../../transform-helper'; 4 | import { assert } from '../../../util/types'; 5 | import AbstractEOComputedProp from './abstract'; 6 | 7 | /** 8 | * Ember Object Computed Function Expression Property 9 | * 10 | * A wrapper object for Ember Object properties where the value is a 11 | * `CallExpression` with a `FunctionExpression` as its last argument. 12 | * 13 | * These represent computed properties (including computed macros) to be 14 | * transformed into `ClassMethod` getters with their appropriate decorators 15 | * (and any modifiers). 16 | * 17 | * @example 18 | * 19 | * ``` 20 | * const MyObject = EmberObject.extend({ 21 | * firstName: 'Krystan', // EOSimpleProp 22 | * lastName: 'HuffMenne', // EOSimpleProp 23 | * 24 | * fullName1: computed('firstName', 'lastName', function() { 25 | * return `${this.get('firstName')} ${this.get('lastName')}`; 26 | * }), 27 | * 28 | * fullName2: computed('firstName', 'lastName', function() { 29 | * return `${this.get('firstName')} ${this.get('lastName')}`; 30 | * }).volatile(), 31 | * 32 | * fullName3: computed('firstName', 'lastName', function() { 33 | * return `${this.get('firstName')} ${this.get('lastName')}`; 34 | * }).volatile().readOnly(), 35 | * }); 36 | * ``` 37 | * 38 | * transforms into: 39 | * 40 | * ``` 41 | * class MyObject extends EmberObject { 42 | * firstName = 'Krystan'; // EOSimpleProp 43 | * lastName = 'HuffMenne'; // EOSimpleProp 44 | * 45 | * \@computed('firstName', 'lastName') 46 | * get fullName2() { 47 | * return `${this.get('firstName')} ${this.get('lastName')}`; 48 | * } 49 | * 50 | * \@(computed('firstName', 'lastName').volatile()) 51 | * get fullName2() { 52 | * return `${this.get('firstName')} ${this.get('lastName')}`; 53 | * } 54 | * 55 | * get fullName3() { 56 | * return `${this.get('firstName')} ${this.get('lastName')}`; 57 | * } 58 | * } 59 | * ``` 60 | * 61 | * Notably, if the modifiers `volatile` and `readOnly` are used in conjunction, 62 | * a non-computed getter will be returned. 63 | */ 64 | export default class EOComputedFunctionExpressionGetter extends AbstractEOComputedProp { 65 | build(): AST.ClassMethod { 66 | const args = this.arguments; 67 | const lastArg = args[args.length - 1]; 68 | assert( 69 | lastArg && lastArg.type === 'FunctionExpression', 70 | 'expected lastArg to be a FunctionExpression' 71 | ); 72 | return replaceGetterSetterSuperExpressions( 73 | j.classMethod.from({ 74 | kind: 'get', 75 | key: this.key, 76 | params: lastArg.params, 77 | body: lastArg.body, 78 | comments: this.comments, 79 | decorators: this.buildDecorators(), 80 | }), 81 | this.replaceSuperWithUndefined, 82 | this.key 83 | ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /transforms/helpers/eo-prop/private/computed/function-expression-method.ts: -------------------------------------------------------------------------------- 1 | import { default as j } from 'jscodeshift'; 2 | import type * as AST from '../../../ast'; 3 | import { replaceMethodSuperExpressions } from '../../../transform-helper'; 4 | import { assert } from '../../../util/types'; 5 | import AbstractEOComputedProp from './abstract'; 6 | 7 | /** 8 | * Ember Object Computed Function Expression Property 9 | * 10 | * A wrapper object for Ember Object properties where the value is a 11 | * `CallExpression` with a `FunctionExpression` as its last argument. 12 | * 13 | * These represent computed properties (including computed macros) to be 14 | * transformed into `ClassMethod`s with their appropriate decorators (and any 15 | * modifiers). 16 | * 17 | * @example 18 | * 19 | * ``` 20 | * import { observer as watcher } from '@ember/object'; 21 | * 22 | * const MyObject = EmberObject.extend({ 23 | * observedProp: watcher('xyz', function() { 24 | * return 'observed'; 25 | * }), 26 | * }); 27 | * ``` 28 | * 29 | * transforms into: 30 | * 31 | * ``` 32 | * import { observes as watcher } from '@ember-decorators/object'; 33 | * 34 | * class MyObject extends EmberObject { 35 | * @watcher('xyz') 36 | * observedProp() { 37 | * return 'observed'; 38 | * } 39 | * } 40 | * ``` 41 | * 42 | * Notably, if the modifiers `volatile` and `readOnly` are used in conjunction, 43 | * a non-computed getter will be returned. 44 | */ 45 | export default class EOComputedFunctionExpressionMethod extends AbstractEOComputedProp { 46 | build(): AST.ClassMethod { 47 | const args = this.arguments; 48 | const lastArg = args[args.length - 1]; 49 | assert( 50 | lastArg && lastArg.type === 'FunctionExpression', 51 | 'expected lastArg to be a FunctionExpression' 52 | ); 53 | return replaceMethodSuperExpressions( 54 | j.classMethod.from({ 55 | kind: 'method', 56 | key: this.key, 57 | params: lastArg.params, 58 | body: lastArg.body, 59 | comments: this.comments, 60 | decorators: this.buildDecorators(), 61 | }), 62 | this.replaceSuperWithUndefined 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /transforms/helpers/eo-prop/private/computed/index.ts: -------------------------------------------------------------------------------- 1 | import * as AST from '../../../ast'; 2 | import type { 3 | DecoratorImportInfo, 4 | DecoratorImportInfoMap, 5 | } from '../../../decorator-info'; 6 | import type { Options } from '../../../options'; 7 | import { assert } from '../../../util/types'; 8 | import EOComputedFunctionExpressionGetter from './function-expression-getter'; 9 | import EOComputedFunctionExpressionMethod from './function-expression-method'; 10 | import { getModifier } from './modifier-helper'; 11 | import EOComputedObjectExpressionProp from './object-expression'; 12 | import EOComputedProp from './property'; 13 | 14 | /** 15 | * Makes an object representing an Ember Object computed property. 16 | */ 17 | export function makeEOComputedProp( 18 | raw: AST.EOComputedProp, 19 | existingDecoratorImportInfos: DecoratorImportInfoMap, 20 | options: Options 21 | ): 22 | | EOComputedFunctionExpressionGetter 23 | | EOComputedFunctionExpressionMethod 24 | | EOComputedObjectExpressionProp 25 | | EOComputedProp { 26 | let calleeObject = raw.value; 27 | const modifiers = [getModifier(calleeObject)]; 28 | while (AST.isEOMemberExpressionForModifier(calleeObject.callee)) { 29 | calleeObject = calleeObject.callee.object; 30 | modifiers.push(getModifier(calleeObject)); 31 | } 32 | modifiers.reverse(); 33 | modifiers.shift(); 34 | 35 | assert( 36 | AST.isEOCallExpressionInnerCallee(calleeObject), 37 | 'calleeObject should be isEOCallExpressionInnerCallee' 38 | ); 39 | 40 | const calleeName = calleeObject.callee.name; 41 | 42 | const decorators = getDecorators( 43 | raw, 44 | existingDecoratorImportInfos, 45 | calleeName, 46 | options 47 | ); 48 | 49 | const kind = getKind(raw, decorators); 50 | 51 | const args = calleeObject.arguments; 52 | const lastArg = args[args.length - 1]; 53 | if (kind === 'method' && lastArg?.type === 'FunctionExpression') { 54 | return new EOComputedFunctionExpressionMethod( 55 | raw, 56 | calleeObject, 57 | modifiers, 58 | kind, 59 | decorators, 60 | options 61 | ); 62 | } else if (kind === 'get' && lastArg?.type === 'FunctionExpression') { 63 | return new EOComputedFunctionExpressionGetter( 64 | raw, 65 | calleeObject, 66 | modifiers, 67 | kind, 68 | decorators, 69 | options 70 | ); 71 | } else if (kind === 'get' && lastArg?.type === 'ObjectExpression') { 72 | return new EOComputedObjectExpressionProp( 73 | raw, 74 | calleeObject, 75 | modifiers, 76 | kind, 77 | decorators, 78 | options 79 | ); 80 | } 81 | 82 | return new EOComputedProp( 83 | raw, 84 | calleeObject, 85 | modifiers, 86 | kind, 87 | decorators, 88 | options 89 | ); 90 | } 91 | 92 | function getDecorators( 93 | raw: AST.EOComputedProp, 94 | existingDecoratorImportInfos: DecoratorImportInfoMap, 95 | calleeName: string, 96 | options: Options 97 | ): DecoratorImportInfo[] { 98 | const decorators: DecoratorImportInfo[] = []; 99 | const decoratorImportInfo = existingDecoratorImportInfos.get(calleeName); 100 | if (decoratorImportInfo) { 101 | decorators.push(decoratorImportInfo); 102 | } else if ( 103 | options.noTelemetry || 104 | options.runtimeData?.computedProperties.includes(raw.key.name) 105 | ) { 106 | decorators.push({ name: calleeName }); 107 | } 108 | return decorators; 109 | } 110 | 111 | function getKind( 112 | raw: AST.EOComputedProp, 113 | decorators: DecoratorImportInfo[] 114 | ): 'get' | 'method' | undefined { 115 | let kind: 'get' | 'method' | undefined; 116 | const method = !!('method' in raw && raw.method); 117 | 118 | if (decorators.some((d) => d.isComputedDecorator)) { 119 | kind = 'get'; 120 | } 121 | 122 | if (method || decorators.some((d) => d.isMethodDecorator)) { 123 | kind = 'method'; 124 | } 125 | 126 | return kind; 127 | } 128 | -------------------------------------------------------------------------------- /transforms/helpers/eo-prop/private/computed/modifier-helper.ts: -------------------------------------------------------------------------------- 1 | import type * as AST from '../../../ast'; 2 | 3 | export interface CallExpressionModifier { 4 | prop: 5 | | Extract['property'] 6 | | undefined; 7 | args: AST.EOCallExpression['arguments']; 8 | } 9 | 10 | /** 11 | * Get property modifier from the property callee object 12 | */ 13 | export function getModifier( 14 | calleeObject: AST.EOCallExpression 15 | ): CallExpressionModifier { 16 | return { 17 | prop: 18 | 'property' in calleeObject.callee 19 | ? calleeObject.callee.property 20 | : undefined, 21 | args: calleeObject.arguments, 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /transforms/helpers/eo-prop/private/computed/object-expression.ts: -------------------------------------------------------------------------------- 1 | import { default as j } from 'jscodeshift'; 2 | import * as AST from '../../../ast'; 3 | import { replaceGetterSetterSuperExpressions } from '../../../transform-helper'; 4 | import { assert, defined } from '../../../util/types'; 5 | import AbstractEOComputedProp from './abstract'; 6 | 7 | /** 8 | * Ember Object Computed Object Expression Property 9 | * 10 | * A wrapper object for Ember Object properties where the value is a 11 | * `CallExpression` with an `ObjectExpression` as its last argument. 12 | * 13 | * These represent computed properties (including computed macros) to be 14 | * transformed into `ClassMethod` getters and/or setters with their appropriate 15 | * decorators (and any modifiers). 16 | * 17 | * @example 18 | * 19 | * ``` 20 | * const MyObject = EmberObject.extend({ 21 | * firstName: 'Krystan', // EOSimpleProp 22 | * lastName: 'HuffMenne', // EOSimpleProp 23 | * 24 | * fullName: computed('firstName', 'lastName', { 25 | * get(key) { 26 | * return `${this.get('firstName')} ${this.get('lastName')}`; 27 | * }, 28 | * set(key, value) { 29 | * //... 30 | * } 31 | * }) 32 | * }); 33 | * ``` 34 | * 35 | * transforms into: 36 | * 37 | * ``` 38 | * class MyObject extends EmberObject { 39 | * firstName = 'Krystan'; // EOSimpleProp 40 | * lastName = 'HuffMenne'; // EOSimpleProp 41 | * 42 | * \@computed('firstName', 'lastName') 43 | * get fullName() { 44 | * return `${this.get('firstName')} ${this.get('lastName')}`; 45 | * } 46 | * 47 | * set fullName() { 48 | * // ... 49 | * } 50 | * } 51 | * ``` 52 | */ 53 | export default class EOComputedObjectExpressionProp extends AbstractEOComputedProp< 54 | AST.ClassMethod[] 55 | > { 56 | build(): AST.ClassMethod[] { 57 | const args = this.arguments; 58 | const lastArg = args[args.length - 1]; 59 | assert( 60 | lastArg && lastArg.type === 'ObjectExpression', 61 | 'expected lastArg to be a ObjectExpression' 62 | ); 63 | const classMethods = lastArg.properties.map((property) => { 64 | assert(AST.isEOMethod(property), 'expected EOMethod'); 65 | assert( 66 | (['get', 'set'] as const).includes(property.key.name as 'get' | 'set') 67 | ); 68 | const kind = property.key.name as 'get' | 'set'; 69 | const key = this.key; 70 | 71 | const params = [...property.params]; 72 | params.shift(); 73 | 74 | return replaceGetterSetterSuperExpressions( 75 | j.classMethod.from({ 76 | kind, 77 | key, 78 | params, 79 | body: property.body, 80 | comments: property.comments ?? null, 81 | }), 82 | this.replaceSuperWithUndefined, 83 | this.key 84 | ); 85 | }); 86 | 87 | const firstMethod = defined(classMethods[0]); 88 | firstMethod.comments = [ 89 | ...(firstMethod.comments ?? []), 90 | ...(this.comments ?? []), 91 | ]; 92 | firstMethod.decorators = this.buildDecorators(); 93 | 94 | return classMethods; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /transforms/helpers/eo-prop/private/computed/property.ts: -------------------------------------------------------------------------------- 1 | import { default as j } from 'jscodeshift'; 2 | import type * as AST from '../../../ast'; 3 | import AbstractEOComputedProp from './abstract'; 4 | 5 | /** 6 | * Ember Object Computed Property 7 | * 8 | * A wrapper object for Ember Object properties not captured by 9 | * `EOFunctionExpressionProp` or `EOComputedObjectExpressionProp`. 10 | * 11 | * These represent computed properties (including computed macros) to be 12 | * transformed into `ClassProperty`s with their appropriate decorators 13 | * (and any modifiers). 14 | * 15 | * @example 16 | * 17 | * ``` 18 | * const MyObject = EmberObject.extend({ 19 | * firstName: 'Krystan', // EOSimpleProp 20 | * lastName: 'HuffMenne', // EOSimpleProp 21 | * 22 | * fName1: alias('firstName'), 23 | * 24 | * fName2: alias('firstName').readOnly(), 25 | * 26 | * computedMacro: customMacro() 27 | * }); 28 | * ``` 29 | * 30 | * transforms into: 31 | * 32 | * ``` 33 | * class MyObject extends EmberObject { 34 | * firstName = 'Krystan'; // EOSimpleProp 35 | * lastName = 'HuffMenne'; // EOSimpleProp 36 | * 37 | * \@alias('firstName') 38 | * fName1; 39 | * 40 | * \@(alias('firstName').readOnly()) 41 | * fName2; 42 | * 43 | * \@customMacro() 44 | * computedMacro; 45 | * } 46 | * ``` 47 | */ 48 | export default class EOComputedProp extends AbstractEOComputedProp { 49 | build(): AST.ClassProperty { 50 | const classProp = j.classProperty.from({ 51 | key: this.key, 52 | value: null, 53 | comments: this.comments, 54 | computed: this.rawProp.computed ?? false, 55 | }); 56 | 57 | // @ts-expect-error jscodeshift AST types are incorrect 58 | // If this ever gets fixed, check if the builder `.from` method above 59 | // will now take a decorators param. 60 | classProp.decorators = this.buildDecorators(); 61 | 62 | return classProp; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /transforms/helpers/eo-prop/private/function-expression.ts: -------------------------------------------------------------------------------- 1 | import { default as j } from 'jscodeshift'; 2 | import type * as AST from '../../ast'; 3 | import { replaceMethodSuperExpressions } from '../../transform-helper'; 4 | import AbstractEOProp from './abstract'; 5 | 6 | /** 7 | * Ember Object Function Expression Property 8 | * 9 | * A wrapper object for Ember Object properties with `FunctionExpression` values 10 | * to be transformed into `ClassMethod`s. 11 | * 12 | * @example 13 | * 14 | * ``` 15 | * const MyObject = EmberObject.extend({ 16 | * myMethod: () => { 17 | * this._super(...arguments); 18 | * } 19 | * }); 20 | * ``` 21 | * 22 | * transforms into: 23 | * 24 | * ``` 25 | * class MyObject extends EmberObject { 26 | * myMethod() { 27 | * super.myMethod(...arguments); 28 | * } 29 | * } 30 | * ``` 31 | * 32 | * @see EOMethod 33 | */ 34 | export default class EOFunctionExpressionProp extends AbstractEOProp< 35 | AST.EOFunctionExpressionProp, 36 | AST.ClassMethod 37 | > { 38 | readonly isClassDecorator = false as const; 39 | 40 | protected readonly value = this.rawProp.value; 41 | 42 | protected override readonly objectLiteralDecoratorSupport = 43 | 'withVerification' as const; 44 | 45 | build(): AST.ClassMethod { 46 | return replaceMethodSuperExpressions( 47 | j.classMethod.from({ 48 | kind: 'method', 49 | key: this.key, 50 | params: this.value.params, 51 | body: this.value.body, 52 | comments: this.comments, 53 | decorators: this.existingDecorators, 54 | generator: this.value.generator ?? false, 55 | async: this.value.async ?? false, 56 | }), 57 | this.replaceSuperWithUndefined 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /transforms/helpers/eo-prop/private/method.ts: -------------------------------------------------------------------------------- 1 | import { default as j } from 'jscodeshift'; 2 | import type * as AST from '../../ast'; 3 | import { replaceMethodSuperExpressions } from '../../transform-helper'; 4 | import AbstractEOProp from './abstract'; 5 | 6 | /** 7 | * Ember Object Method 8 | * 9 | * A wrapper object for Ember Object methods (including getters and setters) to 10 | * be transformed into `ClassMethod`s. 11 | * 12 | * @example 13 | * 14 | * ``` 15 | * const MyObject = EmberObject.extend({ 16 | * get myGetter() {}, 17 | * 18 | * myMethod() { 19 | * this._super(...arguments); 20 | * } 21 | * }); 22 | * ``` 23 | * 24 | * transforms into: 25 | * 26 | * ``` 27 | * class MyObject extends EmberObject { 28 | * get myGetter() {} 29 | * 30 | * myMethod() { 31 | * super.myMethod(...arguments); 32 | * } 33 | * } 34 | * ``` 35 | * 36 | * @see EOFunctionExpressionProp 37 | */ 38 | export default class EOMethod extends AbstractEOProp< 39 | AST.EOMethod, 40 | AST.ClassMethod 41 | > { 42 | readonly isClassDecorator = false as const; 43 | 44 | protected readonly value = this.rawProp; 45 | 46 | protected override readonly objectLiteralDecoratorSupport = true; 47 | 48 | build(): AST.ClassMethod { 49 | return replaceMethodSuperExpressions( 50 | j.classMethod.from({ 51 | kind: this.kind, 52 | key: this.key, 53 | params: this.params, 54 | body: this.body, 55 | comments: this.comments, 56 | decorators: this.existingDecorators, 57 | generator: this.value.generator ?? false, 58 | async: this.value.async ?? false, 59 | }), 60 | this.replaceSuperWithUndefined 61 | ); 62 | } 63 | 64 | protected get kind(): 'get' | 'set' | 'method' { 65 | return this.value.kind; 66 | } 67 | 68 | protected get params(): AST.EOMethod['params'] { 69 | return this.value.params; 70 | } 71 | 72 | protected get body(): AST.EOMethod['body'] { 73 | return this.value.body; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /transforms/helpers/eo-prop/private/simple.ts: -------------------------------------------------------------------------------- 1 | import { default as j } from 'jscodeshift'; 2 | import * as AST from '../../ast'; 3 | import { createDecoratorWithArgs } from '../../decorator-helper'; 4 | import logger from '../../log-helper'; 5 | import AbstractEOProp from './abstract'; 6 | 7 | /** 8 | * Ember Object Simple Property 9 | * 10 | * A wrapper object for Ember Object properties not captured by the other 11 | * `EOProp`/`EOClassDecorator` classes to be transformed into 12 | * `ClassProperties`s. 13 | * 14 | * @example 15 | * 16 | * ``` 17 | * const MyObject = EmberObject.extend({ 18 | * prop: 'defaultValue', 19 | * boolProp: true, 20 | * numProp: 123, 21 | * [MY_VAL]: 'val', 22 | * queryParams: {}, 23 | * someVal, 24 | * }); 25 | * ``` 26 | * 27 | * transforms into: 28 | * 29 | * ``` 30 | * class MyObject extends EmberObject { 31 | * prop = 'defaultValue'; 32 | * boolProp = true; 33 | * numProp = 123; 34 | * [MY_VAL] = 'val'; 35 | * queryParams = {}; 36 | * someVal = someVal; 37 | * } 38 | * ``` 39 | */ 40 | export default class EOSimpleProp extends AbstractEOProp< 41 | AST.EOSimpleProp, 42 | AST.ClassProperty 43 | > { 44 | readonly isClassDecorator = false as const; 45 | 46 | protected readonly value = this.rawProp.value; 47 | 48 | protected override readonly objectLiteralDecoratorSupport = 49 | 'withVerification' as const; 50 | 51 | build(): AST.ClassProperty { 52 | const classProp = j.classProperty.from({ 53 | key: this.key, 54 | value: 55 | this.hasDecorators || AST.isIdent(this.value, 'undefined') 56 | ? null 57 | : this.value, 58 | comments: this.comments, 59 | computed: this.rawProp.computed ?? false, 60 | }); 61 | 62 | // @ts-expect-error jscodeshift AST types are incorrect 63 | // If this ever gets fixed, check if the builder `.from` method above 64 | // will now take a decorators param. 65 | classProp.decorators = this.buildDecorators(); 66 | 67 | return classProp; 68 | } 69 | 70 | protected override get typeErrors(): string[] { 71 | const errors: string[] = []; 72 | const { classFields } = this.options; 73 | 74 | if (!classFields) { 75 | errors.push(this.makeError("need option '--class-fields=true'")); 76 | } 77 | 78 | if ( 79 | (this.type === 'ObjectExpression' || this.type === 'ArrayExpression') && 80 | !this.options.ignoreLeakingState.includes(this.name) 81 | ) { 82 | errors.push( 83 | this.makeError( 84 | 'value is of type object. For more details: eslint-plugin-ember/avoid-leaking-state-in-ember-objects' 85 | ) 86 | ); 87 | } 88 | 89 | return errors; 90 | } 91 | 92 | private buildDecorators(): AST.Decorator[] { 93 | const decorators: AST.Decorator[] = []; 94 | for (const decorator of this.decorators) { 95 | const decoratorName = decorator.name; 96 | if ('args' in decorator) { 97 | decorators.push(createDecoratorWithArgs(decoratorName, decorator.args)); 98 | } else { 99 | logger.warn(`[${this.name}] Ignored decorator ${decoratorName}`); 100 | } 101 | } 102 | 103 | return [...(this.existingDecorators ?? []), ...decorators]; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /transforms/helpers/log-helper.ts: -------------------------------------------------------------------------------- 1 | import { createLogger, format, transports } from 'winston'; 2 | import { assert, isRecord } from './util/types'; 3 | import { inspect } from 'node:util'; 4 | 5 | const { combine, timestamp, printf } = format; 6 | 7 | const logFormatter = printf(({ level, timestamp, message: raw }) => { 8 | assert(typeof timestamp === 'string'); 9 | let message = `${timestamp} ${level.toLocaleUpperCase()}: `; 10 | if (typeof raw === 'string') { 11 | message += raw; 12 | } else if (isRecord(raw)) { 13 | if (typeof raw['filePath'] === 'string') { 14 | message += `[${raw['filePath']}]`; 15 | } 16 | if (typeof raw['info'] === 'string') { 17 | message += `\n\t${raw['info']}`; 18 | } 19 | if (raw['error'] instanceof Error) { 20 | message += `\n\t${raw['error'].name}: ${raw['error'].message}`; 21 | } 22 | } else { 23 | message += `Unhandled log message type, message=${inspect(raw)}`; 24 | } 25 | 26 | return message; 27 | }); 28 | 29 | const logger = createLogger({ 30 | format: combine(timestamp(), logFormatter), 31 | transports: [new transports.File({ filename: 'codemods.log' })], 32 | }); 33 | 34 | export default logger; 35 | -------------------------------------------------------------------------------- /transforms/helpers/parse-helper.ts: -------------------------------------------------------------------------------- 1 | import { default as j } from 'jscodeshift'; 2 | import path from 'path'; 3 | import * as AST from './ast'; 4 | import { classify, type DecoratorImportSpecs } from './util/index'; 5 | import { assert, defined, isRecord } from './util/types'; 6 | 7 | const DO_NOT_SUFFIX = new Set(['Component', 'Helper', 'EmberObject']); 8 | 9 | // List copied from ember-codemods-telemetry-helpers 10 | const TELEMETRY_TYPES = new Set([ 11 | 'Application', 12 | 'Controller', 13 | 'Route', 14 | 'Component', 15 | 'Service', 16 | 'Helper', 17 | 'Router', 18 | 'Engine', 19 | 'EmberObject', 20 | ]); 21 | 22 | /** 23 | * Get the map of decorators to import other than the computed props, services etc 24 | * which already have imports in the code 25 | */ 26 | export function mergeDecoratorImportSpecs( 27 | newSpecs: DecoratorImportSpecs, 28 | existing: DecoratorImportSpecs 29 | ): DecoratorImportSpecs { 30 | return { 31 | action: existing.action || newSpecs.action, 32 | classNames: existing.classNames || newSpecs.classNames, 33 | classNameBindings: existing.classNameBindings || newSpecs.classNameBindings, 34 | attributeBindings: existing.attributeBindings || newSpecs.attributeBindings, 35 | layout: existing.layout || newSpecs.layout, 36 | templateLayout: existing.templateLayout || newSpecs.templateLayout, 37 | off: existing.off || newSpecs.off, 38 | tagName: existing.tagName || newSpecs.tagName, 39 | observes: existing.observes || newSpecs.observes, 40 | unobserves: existing.unobserves || newSpecs.unobserves, 41 | }; 42 | } 43 | 44 | /** Find the `EmberObject.extend` statements */ 45 | export function getEOExtendExpressionCollection( 46 | root: AST.Collection 47 | ): AST.Collection { 48 | return AST.findPaths(root, j.CallExpression, AST.isEOExtendExpression).filter( 49 | (path: AST.Path) => path.parentPath?.value.type !== 'ClassDeclaration' 50 | ); 51 | } 52 | 53 | /** Return closest parent var declaration statement */ 54 | function getClosestVariableDeclaration( 55 | eoExtendExpressionPath: AST.Path 56 | ): AST.Path | null { 57 | const varDeclarations = j(eoExtendExpressionPath).closest( 58 | j.VariableDeclaration 59 | ); 60 | return AST.getFirstPath(varDeclarations) ?? null; 61 | } 62 | 63 | /** 64 | * Get the expression to replace 65 | * 66 | * It returns either VariableDeclaration or the CallExpression depending on how the object is created 67 | */ 68 | export function getExpressionToReplace( 69 | eoExtendExpressionPath: AST.Path 70 | ): AST.Path | AST.Path { 71 | const varDeclaration = getClosestVariableDeclaration(eoExtendExpressionPath); 72 | const parentValue = eoExtendExpressionPath.parentPath?.value; 73 | const isFollowedByCreate = 74 | isRecord(parentValue) && 75 | isRecord(parentValue.property) && 76 | parentValue.property['name'] === 'create'; 77 | 78 | let expressionToReplace: 79 | | AST.Path 80 | | AST.Path = eoExtendExpressionPath; 81 | if (varDeclaration && !isFollowedByCreate) { 82 | expressionToReplace = varDeclaration; 83 | } 84 | return expressionToReplace; 85 | } 86 | 87 | /** Returns name of class to be created */ 88 | export function getClassName( 89 | eoExtendExpressionPath: AST.Path, 90 | filePath: string, 91 | superClassName: string, 92 | type: string | undefined 93 | ): string { 94 | const varDeclaration = getClosestVariableDeclaration(eoExtendExpressionPath); 95 | if (varDeclaration) { 96 | const firstDeclarator = defined(varDeclaration.value.declarations[0]); 97 | assert( 98 | firstDeclarator.type === 'VariableDeclarator', 99 | 'expected firstDeclarator to be a VariableDeclarator' 100 | ); 101 | 102 | const identifier = firstDeclarator.id; 103 | assert( 104 | identifier.type === 'Identifier', 105 | 'expected firstDeclarator.id to be an Identifier' 106 | ); 107 | 108 | return identifier.name; 109 | } 110 | 111 | let className = classify(path.basename(filePath, 'js')); 112 | 113 | // If type is undefined, this means we couldn't find the telemetry or the user 114 | // is running in NO_TELEMETRY mode. In this case, try to infer the type from 115 | // the super class name. 116 | if (!type) { 117 | superClassName = classify(superClassName); 118 | if (TELEMETRY_TYPES.has(superClassName)) { 119 | type = superClassName; 120 | } 121 | } 122 | 123 | if (type === className) { 124 | className = classify(path.basename(path.dirname(filePath))); 125 | } 126 | 127 | if (type && !DO_NOT_SUFFIX.has(type) && !className.endsWith(type)) { 128 | className = `${className}${type}`; 129 | } 130 | 131 | return className; 132 | } 133 | -------------------------------------------------------------------------------- /transforms/helpers/parse.ts: -------------------------------------------------------------------------------- 1 | import type { ParserOptions } from '@babel/core'; 2 | import { parse } from '@babel/parser'; 3 | import type { Parser } from 'jscodeshift'; 4 | 5 | // Inspired by https://github.com/ember-codemods/ember-component-template-colocation-migrator/blob/50c37e5ab8710ced7815dd6c968af97cade23aa4/lib/utils/js-parser.js#L7 6 | 7 | const options: ParserOptions = { 8 | sourceType: 'module', 9 | allowImportExportEverywhere: true, 10 | tokens: true, 11 | plugins: [ 12 | // Without this, our @classic decorators cause this error: 13 | // > Using the export keyword between a decorator and a class is not allowed. 14 | // > Please use `export @dec class` instead. 15 | // Additionally, we have some users using legacy decorator features, such 16 | // as decorators in object literals 17 | 'decorators-legacy', 18 | 'exportDefaultFrom', 19 | 'typescript', 20 | ], 21 | }; 22 | 23 | const parser: Parser = { 24 | parse(code) { 25 | return parse(code, options); 26 | }, 27 | }; 28 | 29 | export default parser; 30 | -------------------------------------------------------------------------------- /transforms/helpers/runtime-data.ts: -------------------------------------------------------------------------------- 1 | import { getTelemetryFor } from 'ember-codemods-telemetry-helpers'; 2 | import path from 'path'; 3 | import { z } from 'zod'; 4 | import logger from './log-helper'; 5 | import { isRecord } from './util/types'; 6 | 7 | const RuntimeDataSchema = z.object({ 8 | type: z.string().optional(), 9 | computedProperties: z.array(z.string()).default([]), 10 | offProperties: z.record(z.array(z.string())).default({}), 11 | overriddenActions: z.array(z.string()).default([]), 12 | overriddenProperties: z.array(z.string()).default([]), 13 | unobservedProperties: z.record(z.array(z.string())).default({}), 14 | observerProperties: z.record(z.array(z.string())).default({}), 15 | }); 16 | 17 | export type RuntimeData = z.infer; 18 | 19 | /** 20 | * Gets telemetry data for the file and parses it into a valid `RuntimeData` 21 | * object. 22 | */ 23 | export function getRuntimeData(filePath: string): RuntimeData | null { 24 | const rawTelemetry = getTelemetryFor(path.resolve(filePath)); 25 | if (!isRecord(rawTelemetry) || !('type' in rawTelemetry)) { 26 | // Do not re-throw. The most likely reason this happened was because 27 | // the user's app threw an error. We still want the codemod to work if so. 28 | logger.error({ 29 | filePath, 30 | error: new RuntimeDataError('Could not find runtime data'), 31 | }); 32 | return null; 33 | } 34 | 35 | const result = RuntimeDataSchema.safeParse(rawTelemetry); 36 | if (result.success) { 37 | return result.data; 38 | } else { 39 | const { errors } = result.error; 40 | const messages = errors.map((error) => { 41 | return `[${error.path.join('.')}]: ${error.message}`; 42 | }); 43 | throw new RuntimeDataError( 44 | `Could not parse runtime data: \n\t${messages.join('\n\t')}` 45 | ); 46 | } 47 | } 48 | 49 | class RuntimeDataError extends Error { 50 | constructor(message: string) { 51 | super(message); 52 | this.name = 'RuntimeDataError'; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /transforms/helpers/schema-helper.ts: -------------------------------------------------------------------------------- 1 | import { inspect } from 'node:util'; 2 | import type { ZodEffects, ZodTypeAny } from 'zod'; 3 | import { z } from 'zod'; 4 | 5 | const preprocessStringToBoolean = ( 6 | schema: I 7 | ): ZodEffects => { 8 | return z.preprocess(strictCoerceStringToBoolean, schema); 9 | }; 10 | 11 | /** Allows true | false | 'true' | 'false' */ 12 | export const StringBooleanSchema = preprocessStringToBoolean(z.boolean()); 13 | 14 | /** Allows false | 'false' */ 15 | export const StringFalseSchema = preprocessStringToBoolean(z.literal(false)); 16 | 17 | function strictCoerceStringToBoolean(arg: unknown): unknown { 18 | return typeof arg === 'string' 19 | ? { true: true, false: false }[arg] ?? arg // strictly coerce 'true' and 'false' to true and false 20 | : arg; 21 | } 22 | 23 | /** Allows an array of strings or a comma-separated string */ 24 | export const StringArraySchema = z.preprocess( 25 | (arg: unknown) => { 26 | return typeof arg === 'string' ? arg.split(/\s*,\s*/) : arg; 27 | }, 28 | z.array(z.string(), { 29 | errorMap: (issue, ctx) => { 30 | if (issue.code === z.ZodIssueCode.invalid_type) { 31 | return { 32 | message: `Expected array of strings or comma-separated string, received ${inspect( 33 | ctx.data 34 | )}`, 35 | }; 36 | } 37 | return { message: ctx.defaultError }; 38 | }, 39 | }) 40 | ); 41 | -------------------------------------------------------------------------------- /transforms/helpers/transform.ts: -------------------------------------------------------------------------------- 1 | import type * as AST from './ast'; 2 | import type { TransformResult } from './eo-extend-expression'; 3 | import EOExtendExpression from './eo-extend-expression'; 4 | import { 5 | createDecoratorImportDeclarations, 6 | getDecoratorImportInfos as getExistingDecoratorImportInfos, 7 | } from './import-helper'; 8 | import logger from './log-helper'; 9 | import type { Options, UserOptions } from './options'; 10 | import { 11 | getEOExtendExpressionCollection, 12 | mergeDecoratorImportSpecs, 13 | } from './parse-helper'; 14 | import { getRuntimeData } from './runtime-data'; 15 | import type { DecoratorImportSpecs } from './util/index'; 16 | 17 | /** Main entry point for parsing and replacing ember objects */ 18 | export default function maybeTransformEmberObjects( 19 | root: AST.Collection, 20 | filePath: string, 21 | userOptions: UserOptions 22 | ): boolean { 23 | const { results, decoratorImportSpecs } = _maybeTransformEmberObjects( 24 | root, 25 | filePath, 26 | userOptions 27 | ); 28 | 29 | for (const result of results) { 30 | if (!result.success) { 31 | throw new ValidationError( 32 | `Validation errors for class '${ 33 | result.className 34 | }':\n\t\t${result.errors.join('\n\t\t')}` 35 | ); 36 | } 37 | } 38 | 39 | const decoratorsToImport = Object.keys(decoratorImportSpecs).filter( 40 | (key) => decoratorImportSpecs[key as keyof DecoratorImportSpecs] 41 | ); 42 | createDecoratorImportDeclarations(root, decoratorsToImport, userOptions); 43 | 44 | return results.length > 0 && results.every((r) => r.success); 45 | } 46 | 47 | function _maybeTransformEmberObjects( 48 | root: AST.Collection, 49 | filePath: string, 50 | userOptions: UserOptions 51 | ): { 52 | results: TransformResult[]; 53 | decoratorImportSpecs: DecoratorImportSpecs; 54 | } { 55 | // Parse the import statements 56 | const existingDecoratorImportInfos = getExistingDecoratorImportInfos(root); 57 | const results: TransformResult[] = []; 58 | let decoratorImportSpecs: DecoratorImportSpecs = { 59 | action: false, 60 | classNames: false, 61 | classNameBindings: false, 62 | attributeBindings: false, 63 | layout: false, 64 | templateLayout: false, 65 | off: false, 66 | tagName: false, 67 | observes: false, 68 | unobserves: false, 69 | }; 70 | 71 | const eoExtendExpressionPaths = getEOExtendExpressionCollection(root); 72 | 73 | if (eoExtendExpressionPaths.length === 0) { 74 | logger.info({ 75 | filePath, 76 | info: "UNMODIFIED: Did not find any 'EmberObject.extend()' expressions", 77 | }); 78 | } else { 79 | const options: Options = { 80 | ...userOptions, 81 | runtimeData: userOptions.noTelemetry ? null : getRuntimeData(filePath), 82 | }; 83 | 84 | // eslint-disable-next-line unicorn/no-array-for-each 85 | eoExtendExpressionPaths.forEach((eoExtendExpressionPath) => { 86 | const extendExpression = new EOExtendExpression( 87 | eoExtendExpressionPath, 88 | filePath, 89 | existingDecoratorImportInfos, 90 | options 91 | ); 92 | 93 | const result = extendExpression.transform(); 94 | results.push(result); 95 | 96 | if (result.success) { 97 | decoratorImportSpecs = mergeDecoratorImportSpecs( 98 | extendExpression.decoratorImportSpecs, 99 | decoratorImportSpecs 100 | ); 101 | } 102 | }); 103 | } 104 | 105 | return { results, decoratorImportSpecs }; 106 | } 107 | 108 | class ValidationError extends Error { 109 | constructor(message: string) { 110 | super(message); 111 | this.name = 'ValidationError'; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /transforms/helpers/util/types.ts: -------------------------------------------------------------------------------- 1 | /** Type predicate. Checks if the given value is a `Record`. */ 2 | export function isRecord>( 3 | value: unknown 4 | ): value is R { 5 | return value !== null && typeof value === 'object'; 6 | } 7 | 8 | /** Type predicate. */ 9 | export function isString(value: unknown): value is string { 10 | return typeof value === 'string'; 11 | } 12 | 13 | /** Assertion function. Throws if the given condition is falsy */ 14 | export function assert( 15 | condition: unknown, 16 | message = 'Assertion Error' 17 | ): asserts condition { 18 | if (!condition) { 19 | throw new Error(message); 20 | } 21 | } 22 | 23 | /** Asserts that the given value matches the given condition before returning it. */ 24 | export function verified( 25 | value: unknown, 26 | condition: (value: unknown) => value is T, 27 | message = condition.name 28 | ? `Verification Error: ${condition.name}` 29 | : 'Verification Error' 30 | ): T { 31 | assert(condition(value), message); 32 | return value; 33 | } 34 | 35 | /** Asserts that the given value is defined before returning it. */ 36 | export function defined( 37 | value: T | undefined, 38 | message = 'Assert Exists Error' 39 | ): T { 40 | assert(value !== undefined, message); 41 | return value; 42 | } 43 | 44 | /** 45 | * Wraps Array.map in a type-check that the initial array has at least two 46 | * items, then returns an array asserting that it has the same number of items. 47 | */ 48 | export function twoOrMoreMap( 49 | array: readonly [T, T, ...T[]], 50 | callbackfn: (value: T, index: number, array: readonly T[]) => U 51 | ): [U, U, ...U[]] { 52 | return array.map(callbackfn) as unknown as [U, U, ...U[]]; 53 | } 54 | -------------------------------------------------------------------------------- /transforms/helpers/validation-helper.ts: -------------------------------------------------------------------------------- 1 | import { minimatch } from 'minimatch'; 2 | import { TYPES, type Type } from './options'; 3 | 4 | const TYPE_PATTERNS = Object.fromEntries( 5 | TYPES.map((type) => [type, `**/${type}/**/*.js`] as const) 6 | ) as Record; 7 | 8 | const TEST_FILE_PATTERN = '**/*-test.js' as const; 9 | 10 | /** Returns true if the specified file is a test file */ 11 | export function isTestFile(file: string): boolean { 12 | return minimatch(file, TEST_FILE_PATTERN); 13 | } 14 | 15 | /** 16 | * Returns true if the given path matches the type of ember object 17 | * The glob patterns are specified by `TYPE_PATTERNS` 18 | */ 19 | export function isFileOfType(file: string, type: Type): boolean { 20 | return minimatch(file, TYPE_PATTERNS[type]); 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node12/tsconfig.json", 3 | "compilerOptions": { 4 | "paths": { 5 | "*": ["./types/*"] 6 | }, 7 | "checkJs": false, 8 | "noEmitOnError": false, 9 | "skipLibCheck": false, 10 | "resolveJsonModule": true, 11 | 12 | "strict": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "allowUnusedLabels": false, 15 | "allowUnreachableCode": false, 16 | "exactOptionalPropertyTypes": true, 17 | "noFallthroughCasesInSwitch": true, 18 | "noImplicitOverride": true, 19 | "noImplicitReturns": true, 20 | "noPropertyAccessFromIndexSignature": true, 21 | "noUncheckedIndexedAccess": true, 22 | "noUnusedLocals": true, 23 | "noUnusedParameters": true 24 | }, 25 | "include": ["transforms/**/*"], 26 | "exclude": ["transforms/ember-object/__testfixtures__/**/*"] 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig.lint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["transforms/**/*", "test/**/*.ts"], 4 | "exclude": ["transforms/ember-object/__testfixtures__/**/*"] 5 | } 6 | -------------------------------------------------------------------------------- /types/codemod-cli.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'codemod-cli' { 2 | export function getOptions(): unknown; 3 | export function runTransformTest(options: Record): unknown; 4 | } 5 | -------------------------------------------------------------------------------- /types/ember-codemods-telemetry-helpers.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'ember-codemods-telemetry-helpers' { 2 | export function getTelemetryFor(filePath: string): unknown; 3 | export function setTelemetry(telemetry: unknown): void; 4 | } 5 | -------------------------------------------------------------------------------- /types/jscodeshift/dist/testUtils.d.ts: -------------------------------------------------------------------------------- 1 | import type { FileInfo, Options, Parser, Transform } from 'jscodeshift'; 2 | 3 | declare module 'jscodeshift/dist/testUtils' { 4 | export function applyTransform( 5 | transform: Transform, 6 | transformOptions: Options, 7 | fileInfo: FileInfo, 8 | testOptions?: { parser: Parser } 9 | ): string; 10 | } 11 | --------------------------------------------------------------------------------