├── .commitlintrc.json ├── .eslintignore ├── .eslintrc.js ├── .flowconfig ├── .github └── workflows │ ├── commit.yml │ ├── release-auto.yml │ └── release-manual.yml ├── .gitignore ├── .husky ├── pre-commit ├── pre-push └── skip.js ├── .prettierrc.yml ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── lerna.json ├── package-lock.json ├── package.json └── packages ├── plugin ├── .babelrc ├── .eslintignore ├── .npmignore ├── CHANGELOG.md ├── README.md ├── __test__ │ ├── __snapshots__ │ │ └── test.js.snap │ ├── fixtures │ │ ├── classes │ │ │ ├── class-computed-prop.js │ │ │ ├── class-convert-all.js │ │ │ ├── class-convert-calling-super-apply.controller.js │ │ │ ├── class-convert-constructor-keep-annot.controller.js │ │ │ ├── class-convert-constructor-keep-default.controller.js │ │ │ ├── class-convert-constructor-move.controller.js │ │ │ ├── class-convert-constructor-noargs.js │ │ │ ├── class-convert-constructor-spread.js │ │ │ ├── class-convert-controller-basic.controller.js │ │ │ ├── class-convert-controller-extend-static-assign.js │ │ │ ├── class-convert-controller-extend-static-prop.controller.js │ │ │ ├── class-convert-controller-extension-static-prop-compatibility.controller.js │ │ │ ├── class-convert-controller-extension-static-prop.controller.js │ │ │ ├── class-convert-controller-multi.controller.js │ │ │ ├── class-convert-controller-w-oninit.controller.js │ │ │ ├── class-convert-controller-wo-oninit.controller.js │ │ │ ├── class-convert-dec-alias.js │ │ │ ├── class-convert-dec-name.js │ │ │ ├── class-convert-dec-namespace.controller.js │ │ │ ├── class-convert-dec-nonui5.js │ │ │ ├── class-convert-extend-managed.js │ │ │ ├── class-convert-file-namespace-prefixed.controller.js │ │ │ ├── class-convert-file-namespace.controller.controller.js │ │ │ ├── class-convert-jsdoc-alias.js │ │ │ ├── class-convert-jsdoc-name.js │ │ │ ├── class-convert-jsdoc-namespace.controller.js │ │ │ ├── class-convert-jsdoc-namespace.js │ │ │ ├── class-convert-jsdoc-nonui5.js │ │ │ ├── class-convert-metadata-assign-nested.js │ │ │ ├── class-convert-metadata-assign.js │ │ │ ├── class-convert-metadata-prop.js │ │ │ ├── class-convert-metadata-static.js │ │ │ ├── class-convert-never.controller.js │ │ │ ├── class-convert-no-constructor.js │ │ │ ├── class-convert-nonui5-return.js │ │ │ ├── class-convert-only-move-this-binding.js │ │ │ ├── class-convert-options-namedonly.js │ │ │ ├── class-convert-with-existing-define-defer-return.js │ │ │ ├── class-convert-with-existing-define-immediate-return.js │ │ │ ├── class-fix-with-existing-define.js │ │ │ └── class-import-meta.js │ │ ├── classic │ │ │ ├── sap-ui-define-copyright-file.ts │ │ │ ├── sap-ui-define-copyright.ts │ │ │ └── sap-ui-define.ts │ │ ├── comments │ │ │ ├── copyright-file.js │ │ │ ├── copyright-multiple-comments.js │ │ │ ├── copyright.js │ │ │ ├── min-wrap-no-import-copyright.js │ │ │ ├── min-wrap-simple-copyright.js │ │ │ ├── min-wrap-ui5-copyright.js │ │ │ └── preserve-for-class-members.js │ │ ├── decorators │ │ │ ├── mixin.js │ │ │ ├── mixin_mixed.js │ │ │ └── ts-class-controller-extension-usage-decorator.ts │ │ ├── empty │ │ │ ├── empty_js.js │ │ │ └── empty_ts.ts │ │ ├── examples │ │ │ ├── Animal.js │ │ │ ├── Cat.js │ │ │ ├── MyError.js │ │ │ └── Utils.js │ │ ├── export-collapse │ │ │ ├── _extends-literal.js │ │ │ ├── _extends-variable.js │ │ │ ├── add-props-obj.js │ │ │ ├── object.assign-literal.js │ │ │ └── object.assign-variable.js │ │ ├── export-error │ │ │ ├── error-assign-var-recurs.js │ │ │ ├── error-assign-var.js │ │ │ ├── error-obj-method.js │ │ │ └── error-obj-prop.js │ │ ├── export-mixed │ │ │ ├── export-mixed-anon-func.js │ │ │ ├── export-mixed-arrow.js │ │ │ ├── export-mixed-literal.js │ │ │ └── export-mixed-named-func.js │ │ ├── exports │ │ │ ├── export-class-extend-native.js │ │ │ ├── export-class.js │ │ │ ├── export-default-anon-function.js │ │ │ ├── export-default-arrow-function.js │ │ │ ├── export-default-identifier.js │ │ │ ├── export-default-named-function.js │ │ │ ├── export-multiple.js │ │ │ ├── export-named-as-default.js │ │ │ ├── export-named-from.js │ │ │ └── export-obj.js │ │ ├── flow │ │ │ └── flow-exports.js │ │ ├── imports │ │ │ ├── _private_ │ │ │ │ ├── module_js.js │ │ │ │ ├── module_jsx.jsx │ │ │ │ ├── module_ts.ts │ │ │ │ └── module_tsx.tsx │ │ │ ├── import-all.js │ │ │ ├── import-at-sign.js │ │ │ ├── import-duplicate-src-1.js │ │ │ ├── import-duplicate-src-2.js │ │ │ ├── import-duplicate-src-3.js │ │ │ ├── import-dynamic.js │ │ │ ├── import-modules-map-fn.js │ │ │ ├── import-modules-map.js │ │ │ ├── import-multiple.js │ │ │ └── import-remove-fileext.js │ │ ├── libs │ │ │ ├── libs-other-imports.js │ │ │ ├── libs-sap-imports-dashes.js │ │ │ └── libs-sap-imports.js │ │ ├── min-wrap │ │ │ ├── min-wrap-no-import.js │ │ │ ├── min-wrap-simple.js │ │ │ ├── min-wrap-test-wrap.js │ │ │ ├── min-wrap-test.js │ │ │ └── min-wrap-ui5.js │ │ ├── mixed │ │ │ └── mixed-import-export.js │ │ ├── never-use-strict │ │ │ ├── use-strict-define.js │ │ │ └── use-strict-program.js │ │ ├── no-wrap │ │ │ ├── nowrap-existing-sap.ui.define.js │ │ │ └── skip-iffe.js │ │ ├── preset-env │ │ │ ├── preset-env-class.js │ │ │ ├── preset-env-property-mutators.js │ │ │ └── preset-env-usage.js │ │ ├── sap-ui-require │ │ │ ├── othermodule-annotation-end.js │ │ │ ├── othermodule-annotation-middle.js │ │ │ ├── othermodule-annotation-nested.js │ │ │ ├── othermodule-annotation.js │ │ │ ├── othermodule-noannotation.js │ │ │ ├── testsuite-annotation-with-qunit-import.qunit.js │ │ │ ├── testsuite-annotation.qunit.js │ │ │ └── testsuite-noannotation.qunit.js │ │ ├── typescript-preset-env │ │ │ ├── ts-class-anonymous-copyright-file.ts │ │ │ ├── ts-class-anonymous-copyright.ts │ │ │ ├── ts-class-anonymous.ts │ │ │ ├── ts-class-param-props-copyright-file.ts │ │ │ ├── ts-class-param-props-copyright.ts │ │ │ └── ts-class-param-props.ts │ │ ├── typescript │ │ │ ├── _private_ │ │ │ │ ├── index.ts │ │ │ │ └── type.ts │ │ │ ├── ts-class-anonymous-copyright-file.ts │ │ │ ├── ts-class-anonymous-copyright.ts │ │ │ ├── ts-class-anonymous.ts │ │ │ ├── ts-class-controller-extension-extended-error-1.ts │ │ │ ├── ts-class-controller-extension-extended-error-2.ts │ │ │ ├── ts-class-controller-extension-extended-explicit-type.ts │ │ │ ├── ts-class-controller-extension-extended-renamed.ts │ │ │ ├── ts-class-controller-extension-extended.ts │ │ │ ├── ts-class-controller-extension-usage-new.ts │ │ │ ├── ts-class-controller-extension-usage.ts │ │ │ ├── ts-class-controller-extension-wrapped.ts │ │ │ ├── ts-class-param-props-copyright-file.ts │ │ │ ├── ts-class-param-props-copyright.ts │ │ │ ├── ts-class-param-props.ts │ │ │ ├── ts-class-props-only-move-this.ts │ │ │ ├── ts-class-props.ts │ │ │ ├── ts-export-default-typed-anon-fn-only-default.ts │ │ │ ├── ts-export-default-typed-anon-obj.ts │ │ │ ├── ts-export-default-typed-arrow-fn-only-default.ts │ │ │ ├── ts-export-default-typed-arrow-fn-with-named.1.ts │ │ │ ├── ts-export-interface.ts │ │ │ ├── ts-export-type-only.ts │ │ │ ├── ts-export-type-reexport-enum.ts │ │ │ ├── ts-export-type-reexport.ts │ │ │ ├── ts-export-type.ts │ │ │ ├── ts-index.ts │ │ │ ├── ts-re-export-type-only.ts │ │ │ └── ts-re-export.ts │ │ └── wrapping │ │ │ ├── export-const.js │ │ │ ├── export-jsdoc-global.js │ │ │ ├── export-literal.js │ │ │ └── export-options-global.js │ ├── options.js │ └── test.js ├── package.json └── src │ ├── classes │ ├── helpers │ │ ├── classes.js │ │ ├── decorators.js │ │ ├── imports.js │ │ └── jsdoc.js │ ├── pre.js │ └── visitor.js │ ├── index.js │ ├── modules │ ├── helpers │ │ ├── exports.js │ │ └── wrapper.js │ └── visitor.js │ └── utils │ ├── ast.js │ └── templates.js └── preset ├── .babelrc ├── .npmignore ├── CHANGELOG.md ├── README.md ├── index.js └── package.json /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@commitlint/config-conventional"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | __test__/fixtures/** 2 | __test__/__output__/** 3 | build/ 4 | dist/ -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["plugin:prettier/recommended", "eslint:recommended"], 3 | parser: "@babel/eslint-parser", 4 | parserOptions: { 5 | sourceType: "module", 6 | requireConfigFile: false, 7 | }, 8 | env: { 9 | node: true, 10 | }, 11 | rules: { 12 | "prettier/prettier": "warn", 13 | "no-unused-vars": "warn", 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | 3 | [include] 4 | 5 | [libs] 6 | 7 | [lints] 8 | 9 | [options] 10 | -------------------------------------------------------------------------------- /.github/workflows/commit.yml: -------------------------------------------------------------------------------- 1 | name: "Validate PR/Push" 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | HUSKY_SKIP: true 11 | 12 | jobs: 13 | commitlint: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | fetch-depth: 0 19 | - uses: wagoid/commitlint-github-action@v4 20 | 21 | validate: 22 | runs-on: ubuntu-latest 23 | if: "! contains(github.event.head_commit.message, '[skip ci]')" 24 | strategy: 25 | matrix: 26 | node-version: [16.x, 18.x, 20.x] 27 | steps: 28 | - uses: actions/checkout@v3 29 | - name: Use Node.js ${{ matrix.node-version }} 30 | uses: actions/setup-node@v3 31 | with: 32 | node-version: ${{ matrix.node-version }} 33 | - run: | 34 | npm ci 35 | npm test 36 | -------------------------------------------------------------------------------- /.github/workflows/release-auto.yml: -------------------------------------------------------------------------------- 1 | name: Release (automatic) 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" # Push events to matching v*, i.e. v1.0, v20.15.10 7 | 8 | env: 9 | HUSKY_SKIP: true 10 | 11 | jobs: 12 | release: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v3 16 | with: 17 | fetch-depth: 0 18 | 19 | - uses: actions/setup-node@v3 20 | with: 21 | node-version: "16" 22 | 23 | - name: Install dependencies 24 | run: npm ci 25 | 26 | - name: Publish to NPM 27 | run: | 28 | echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc 29 | echo ":::: Publishing as $(npm whoami)" 30 | npm run release:publish 31 | env: 32 | NPM_TOKEN: ${{ secrets.NPM_RELEASE_AUTH_TOKEN }} 33 | -------------------------------------------------------------------------------- /.github/workflows/release-manual.yml: -------------------------------------------------------------------------------- 1 | name: Release (manually) 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | env: 7 | HUSKY_SKIP: true 8 | 9 | jobs: 10 | release: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | with: 15 | fetch-depth: 0 16 | 17 | - uses: actions/setup-node@v3 18 | with: 19 | node-version: "16" 20 | 21 | - name: Install dependencies 22 | run: npm ci 23 | 24 | - name: Publish to NPM 25 | run: | 26 | echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc 27 | echo ":::: Publishing as $(npm whoami)" 28 | npm run release:publish-manual 29 | env: 30 | NPM_TOKEN: ${{ secrets.NPM_RELEASE_AUTH_TOKEN }} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Others 2 | .DS_Store 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | 35 | # Test Output 36 | __output__ 37 | __private__ 38 | 39 | dist/ 40 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npm run hooks:pre-commit 2 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | npm run hooks:pre-push 2 | -------------------------------------------------------------------------------- /.husky/skip.js: -------------------------------------------------------------------------------- 1 | if (process.env.HUSKY_SKIP) { 2 | process.exit(0); 3 | } else { 4 | process.exit(1); 5 | } 6 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | trailingComma: "es5" 2 | tabWidth: 2 3 | semi: true 4 | singleQuote: false 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "javascript.validate.enable": false, 3 | "flow.enabled": true, 4 | "cSpell.words": [ 5 | "deconstructor", 6 | "deconstructors", 7 | "destructure", 8 | "interop", 9 | "interops", 10 | "nonui", 11 | "preload", 12 | "sapui", 13 | "sergiirocks", 14 | "sourceroot", 15 | "supername" 16 | ], 17 | "editor.formatOnSave": true 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Ryan Murphy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "7.7.1", 3 | "packages": [ 4 | "packages/*" 5 | ], 6 | "command": { 7 | "publish": { 8 | "conventionalCommits": true, 9 | "ignoreChanges": [ 10 | "*.md", 11 | "*.json", 12 | "*.txt", 13 | "test/**", 14 | "codemods/**", 15 | "package.json" 16 | ] 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-ui5", 3 | "private": true, 4 | "devDependencies": { 5 | "@commitlint/cli": "^19.5.0", 6 | "@commitlint/config-conventional": "^19.5.0", 7 | "cz-conventional-changelog": "^3.3.0", 8 | "eslint": "^8.54.0", 9 | "eslint-config-prettier": "^9.0.0", 10 | "eslint-plugin-prettier": "^5.0.1", 11 | "husky": "^9.1.6", 12 | "lerna": "^8" 13 | }, 14 | "scripts": { 15 | "prepare": "npm run prepare:husky && npm run prepare:workspace", 16 | "prepare:husky": "node ./.husky/skip.js || husky install", 17 | "prepare:workspace": "lerna run clean && lerna run lint && lerna run build && lerna run test", 18 | "hooks:pre-commit": "lerna run lint:staged && lerna run test", 19 | "hooks:pre-push": "npm run lint:commit", 20 | "clean": "lerna run clean", 21 | "build": "lerna run build", 22 | "lint": "lerna run lint", 23 | "lint:commit": "commitlint -e", 24 | "test": "lerna run test", 25 | "test:update-snapshot": "lerna run test:update-snapshot", 26 | "release:version": "lerna version", 27 | "release:publish": "lerna publish from-git --yes --no-verify-access", 28 | "release:publish-manual": "lerna publish from-package --yes --no-verify-access" 29 | }, 30 | "workspaces": [ 31 | "packages/*" 32 | ] 33 | } -------------------------------------------------------------------------------- /packages/plugin/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | ], 4 | "presets": [ 5 | ["@babel/preset-env", { 6 | "targets": { 7 | "node": "6.10" 8 | } 9 | }] 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /packages/plugin/.eslintignore: -------------------------------------------------------------------------------- 1 | /__test__/fixtures/ 2 | /__test__/__output__/ 3 | build/ 4 | coverage/ 5 | dist/ 6 | -------------------------------------------------------------------------------- /packages/plugin/.npmignore: -------------------------------------------------------------------------------- 1 | __test__ 2 | node_modules/ 3 | coverage/ 4 | src/ 5 | .babelrc 6 | .eslintignore 7 | -------------------------------------------------------------------------------- /packages/plugin/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [7.7.1](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.7.0...v7.7.1) (2025-05-13) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * autoConvertControllerClass to consider controller.ts file as well (not just .js) ([#140](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/140)) ([788705d](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/788705dde44091cfecb47e9f51a5bd294b39af4b)), closes [#139](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/139) 12 | * noWrapBeforeImport must wrap first import if no code is before ([#141](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/141)) ([d80c070](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/d80c070e181b03546d381d8ee2fb0ddfc09d4e5a)) 13 | 14 | 15 | 16 | 17 | 18 | # [7.7.0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.6.2...v7.7.0) (2024-09-16) 19 | 20 | 21 | ### Features 22 | 23 | * update dependencies / add peer dependency to @babel/core ([#135](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/135)) ([d155e48](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/d155e48a4a3737ebcf4a7e77318391bbce783e70)), closes [#133](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/133) 24 | 25 | 26 | 27 | 28 | 29 | ## [7.6.2](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.6.1...v7.6.2) (2024-09-14) 30 | 31 | 32 | ### Bug Fixes 33 | 34 | * move qunit config autostart out of require/define ([#134](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/134)) ([9643cd0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/9643cd08314ce54816886df6c618fbb142c1b3e8)) 35 | 36 | 37 | 38 | 39 | 40 | ## [7.6.1](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.6.0...v7.6.1) (2024-09-13) 41 | 42 | 43 | ### Bug Fixes 44 | 45 | * support wrapped controller extension assignments ([dfa51b6](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/dfa51b613259aeedb3f20987f5e5165ca35545d8)) 46 | 47 | 48 | 49 | 50 | 51 | # [7.6.0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.5.0...v7.6.0) (2024-07-22) 52 | 53 | 54 | ### Features 55 | 56 | * support sap.ui.require for [@sap](https://github.com/sap)UiRequire annotated modules ([#131](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/131)) ([23c4ac6](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/23c4ac67eef5a825692c528852ef62bbd621d13e)) 57 | 58 | 59 | 60 | 61 | 62 | # [7.5.0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.4.2...v7.5.0) (2024-07-12) 63 | 64 | 65 | ### Features 66 | 67 | * support ControllerExtensions extended with .override(...) ([#128](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/128)) ([f744d9d](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/f744d9db3dd82020712b47a18ce55ed51a13fe89)) 68 | 69 | 70 | 71 | 72 | 73 | ## [7.4.2](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.4.1...v7.4.2) (2024-06-25) 74 | 75 | 76 | ### Bug Fixes 77 | 78 | * remove mandatory ESM file extensions from sap.ui.define imports ([#129](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/129)) ([571dfa1](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/571dfa192984c9838c573909546b42a85b71e5b6)) 79 | 80 | 81 | 82 | 83 | 84 | ## [7.4.1](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.4.0...v7.4.1) (2024-06-02) 85 | 86 | 87 | ### Bug Fixes 88 | 89 | * ensure to remove file ext .js only from module path ([#127](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/127)) ([c6afb92](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/c6afb92c2f758a245086baa6f77d3274bb4e5466)) 90 | 91 | 92 | 93 | 94 | 95 | # [7.4.0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.3.1...v7.4.0) (2024-05-31) 96 | 97 | 98 | ### Bug Fixes 99 | 100 | * remove empty export declaration added by TypeScript ([3cb14cf](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/3cb14cf7aefd400d0191d364f457c66452edc32a)), closes [/github.com/babel/babel/blob/main/packages/babel-plugin-transform-typescript/src/index.ts#L399](https://github.com//github.com/babel/babel/blob/main/packages/babel-plugin-transform-typescript/src/index.ts/issues/L399) 101 | 102 | 103 | ### Features 104 | 105 | * imports added by other plugins are also included in the sap.ui.define calls ([#126](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/126)) ([895fe62](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/895fe624df66a6db1b9daa5cadce8a984ced5af4)) 106 | 107 | 108 | 109 | 110 | 111 | ## [7.3.1](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.3.0...v7.3.1) (2024-05-12) 112 | 113 | 114 | ### Bug Fixes 115 | 116 | * remove file extensions from imports to avoid redundant extensions ([#125](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/125)) ([e86d6f6](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/e86d6f675451b6eaaaf63f9433200cd7406d0245)) 117 | * support index modules for dependencies ([#123](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/123)) ([f510380](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/f510380f7c201d9aba3e735badea21546f81f03d)) 118 | 119 | 120 | 121 | 122 | 123 | # [7.3.0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.2.6...v7.3.0) (2024-01-18) 124 | 125 | 126 | ### Bug Fixes 127 | 128 | * sanitize variable names for re-exports ([#122](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/122)) ([f475dbc](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/f475dbcb4f8b767a5680a974903e7f97df8dacc7)) 129 | 130 | 131 | ### Features 132 | 133 | * support using predefined controller extensions ([#120](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/120)) ([d7cb66c](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/d7cb66cd5af422529bb00658669e595af2477615)) 134 | 135 | 136 | 137 | 138 | 139 | ## [7.2.6](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.2.5...v7.2.6) (2023-11-23) 140 | 141 | 142 | ### Bug Fixes 143 | 144 | * preserve comments for class members ([#117](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/117)) ([b5329cb](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/b5329cb80ea0cade6b85fd1d802befa0bc36fe52)) 145 | 146 | 147 | 148 | 149 | 150 | ## [7.2.5](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.2.4...v7.2.5) (2023-08-28) 151 | 152 | 153 | ### Bug Fixes 154 | 155 | * by default add "use strict" directive to sap.ui.define ([#115](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/115)) ([7d55cc6](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/7d55cc6126258b60728a00f4070ed19b8ffe9339)), closes [#113](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/113) 156 | 157 | 158 | 159 | 160 | 161 | ## [7.2.4](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.2.3...v7.2.4) (2023-07-20) 162 | 163 | 164 | ### Bug Fixes 165 | 166 | * ensure template to be ES5 compliant ([#111](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/111)) ([16819bc](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/16819bc91ce1942e0968a0f7efaa4e7cf767b665)) 167 | 168 | 169 | 170 | 171 | 172 | ## [7.2.3](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.2.2...v7.2.3) (2023-07-18) 173 | 174 | 175 | ### Bug Fixes 176 | 177 | * copyright comment must be appended as leading comment to file ([#108](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/108)) ([8194671](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/81946714f1d48c82a6609ec3fc1c820e5fff9e51)) 178 | 179 | 180 | 181 | 182 | 183 | ## [7.2.2](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.2.1...v7.2.2) (2023-07-18) 184 | 185 | 186 | ### Bug Fixes 187 | 188 | * ensure copyright to be kept after typescript processing ([#107](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/107)) ([da9aa69](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/da9aa69c94017e085d7f5a22abde04b123d9307d)) 189 | 190 | 191 | 192 | 193 | 194 | ## [7.2.1](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.2.0...v7.2.1) (2023-06-13) 195 | 196 | 197 | ### Bug Fixes 198 | 199 | * add support for import.meta.url|resolve ([#106](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/106)) ([afb1e6d](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/afb1e6daa0fcc9896fd184ac9d66990d0a29214a)), closes [#103](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/103) 200 | * support for anonymous classes ([#105](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/105)) ([64773d1](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/64773d180bf65e544806b9f04b7334cd76831b3d)), closes [#104](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/104) 201 | 202 | 203 | 204 | 205 | 206 | # [7.2.0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.1.5...v7.2.0) (2023-05-30) 207 | 208 | 209 | ### Features 210 | 211 | * split class conversion to enable other Babel plugin conversion ([#100](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/100)) ([4ba096b](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/4ba096b1a24d807cda2fd2f57425f3ab4b91a31b)), closes [#23](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/23) [#25](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/25) 212 | 213 | 214 | 215 | 216 | 217 | ## [7.1.5](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.1.4...v7.1.5) (2023-05-30) 218 | 219 | 220 | ### Bug Fixes 221 | 222 | * enable support for TS param props ([#99](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/99)) ([2119b41](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/2119b41fbd5fb41ade7e096190cbebad03eecf7c)), closes [#65](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/65) 223 | * ensure copyright comments to be leading comments ([#97](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/97)) ([39ab194](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/39ab1946f08d3fc33609bd6b9ac6cda9a985c5d7)) 224 | * sap.ui.define without callback function ([#98](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/98)) ([c1cd6a8](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/c1cd6a8f158de9b5036d9c768c48972d66ae20aa)), closes [#50](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/50) 225 | 226 | 227 | 228 | 229 | 230 | ## [7.1.4](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.1.3...v7.1.4) (2023-04-11) 231 | 232 | 233 | ### Bug Fixes 234 | 235 | * properly handle dynamic import of non-existing modules ([#94](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/94)) ([c443b36](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/c443b36d5035d2e2fa367e074d0732336af8eb78)) 236 | 237 | 238 | 239 | 240 | 241 | ## [7.1.3](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.1.2...v7.1.3) (2023-04-11) 242 | 243 | 244 | ### Bug Fixes 245 | 246 | * dynamic import must not handle __esModule flagged modules ([#93](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/93)) ([b269985](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/b26998540cac0c2c2868bd59198d6f9abfaab8f7)) 247 | 248 | 249 | 250 | 251 | 252 | ## [7.1.2](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.1.1...v7.1.2) (2023-04-11) 253 | 254 | 255 | ### Bug Fixes 256 | 257 | * dynamic imports of empty modules must not fail ([#92](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/92)) ([a1de75d](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/a1de75dcfcb577bf6fea3669cf448e9183b7d636)) 258 | 259 | 260 | 261 | 262 | 263 | ## [7.1.1](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.1.0...v7.1.1) (2023-03-27) 264 | 265 | 266 | ### Bug Fixes 267 | 268 | * catch err for dynamic import to sap.ui.require ([#91](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/91)) ([9c7d26e](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/9c7d26e6aeebc77f5d8faec941fbd994b77de222)) 269 | 270 | 271 | 272 | 273 | 274 | # [7.1.0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.0.6...v7.1.0) (2023-03-13) 275 | 276 | 277 | ### Features 278 | 279 | * backward compatibility for overrides (overridesToOverride option) ([dac96e6](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/dac96e685548ccf831893c03e083e6db2dae3d4f)) 280 | 281 | 282 | 283 | 284 | 285 | ## [7.0.6](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.0.5...v7.0.6) (2023-02-21) 286 | 287 | **Note:** Version bump only for package babel-plugin-transform-modules-ui5 288 | -------------------------------------------------------------------------------- /packages/plugin/README.md: -------------------------------------------------------------------------------- 1 | 2 | # babel-plugin-transform-modules-ui5 3 | 4 | [Docs](../../README.md) 5 | 6 | There is also a preset which should be used rather than using the plugin directly. 7 | 8 | ## Install 9 | 10 | ```sh 11 | npm install babel-plugin-transform-modules-ui5 --save-dev 12 | ``` 13 | 14 | or 15 | 16 | ```sh 17 | yarn add babel-plugin-transform-modules-ui5 --dev 18 | ``` 19 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-computed-prop.js: -------------------------------------------------------------------------------- 1 | import Controller from "sap/class"; 2 | 3 | const name = Symbol("_name"); 4 | 5 | /** 6 | * @name my.TestController 7 | * @controller 8 | */ 9 | class Test extends Controller { 10 | [name] = null; 11 | [name] = 1; 12 | [name] = function() {}; 13 | [name] = async function() {}; 14 | [name] = () => {}; 15 | [name] = async () => {}; 16 | 17 | static [name] = null; 18 | static [name] = 2; 19 | static [name] = () => {}; 20 | static [name] = function() {}; 21 | static [name] = async () => {}; 22 | static [name] = async function() {}; 23 | } 24 | 25 | export default Test; 26 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-all.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This tests the autoConvertAllExtendClasses flag = true 3 | */ 4 | import SAPClass from "sap/class"; 5 | 6 | class MyClass extends SAPClass {} 7 | 8 | class BaseClass {} 9 | 10 | class MyError extends Error {} 11 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-calling-super-apply.controller.js: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/Controller"; 2 | 3 | export default class MyController extends Controller { 4 | constructor(arg) { 5 | console.log("before super"); 6 | super(arg); 7 | console.log("after super"); 8 | this.x = 1; 9 | } 10 | 11 | method(args) { 12 | super.init.apply(this, args); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-constructor-keep-annot.controller.js: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/Controller"; 2 | 3 | export default class MyController extends Controller { 4 | /** 5 | * @keep 6 | */ 7 | constructor(arg) { 8 | console.log("before super"); 9 | super(arg); 10 | console.log("after super"); 11 | this.x = 1; 12 | } 13 | other = 1; 14 | 15 | ref = this.byId("ref"); 16 | 17 | get getter() { 18 | return 1; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-constructor-keep-default.controller.js: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/Controller"; 2 | 3 | export default class MyController extends Controller { 4 | constructor(arg) { 5 | console.log("before super"); 6 | super(arg); 7 | console.log("after super"); 8 | this.x = 1; 9 | } 10 | other = 1; 11 | 12 | ref = this.byId("ref"); 13 | 14 | get getter() { 15 | return 1; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-constructor-move.controller.js: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/Controller"; 2 | 3 | export default class MyController extends Controller { 4 | constructor(arg) { 5 | console.log("before super"); 6 | super(arg); 7 | console.log("after super"); 8 | this.x = 1; 9 | } 10 | other = 1; 11 | 12 | ref = this.byId("ref"); 13 | get getter() { 14 | return 1; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-constructor-noargs.js: -------------------------------------------------------------------------------- 1 | /* global sap */ 2 | /** 3 | * Test notes: Even though there is an existing sp.ui.define, 4 | * the class should be converted. 5 | */ 6 | sap.ui.define(["sap/SAPClass"], SAPClass => { 7 | /** 8 | * @name com.app.MyClass 9 | */ 10 | return class MyClass extends SAPClass { 11 | constructor() { 12 | super(); 13 | } 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-constructor-spread.js: -------------------------------------------------------------------------------- 1 | /* global sap */ 2 | /** 3 | * Test notes: Even though there is an existing sp.ui.define, 4 | * the class should be converted. 5 | */ 6 | sap.ui.define(["sap/SAPClass"], SAPClass => { 7 | /** 8 | * @name com.app.MyClass 9 | */ 10 | return class MyClass extends SAPClass { 11 | constructor(...args) { 12 | super(...args); 13 | } 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-controller-basic.controller.js: -------------------------------------------------------------------------------- 1 | // This fixture tests the detection of .controller.js files 2 | // and the option to auto-convert these controllers by default. 3 | 4 | import SAPController from "sap/ui/core/Controller"; 5 | 6 | class MyController extends SAPController {} 7 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-controller-extend-static-assign.js: -------------------------------------------------------------------------------- 1 | import SAPController from "sap/SAPController"; 2 | /** 3 | * @name test.fixtures.classes.MyController 4 | */ 5 | export default class MyController extends SAPController {} 6 | const meta = {}; 7 | const Formatter = {}; 8 | /** 9 | * This tests finding the metadata after the class declaration. 10 | * This is how typescript compiles static props. 11 | */ 12 | MyController.metadata = meta; 13 | MyController.renderer = {}; 14 | MyController.myFormatter = Formatter; 15 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-controller-extend-static-prop.controller.js: -------------------------------------------------------------------------------- 1 | import SAPController from "sap/SAPClass"; 2 | 3 | const Formatter = {}; 4 | 5 | export default class MyController extends SAPController { 6 | static metadata = {}; 7 | static myFormatter = Formatter; 8 | } 9 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-controller-extension-static-prop-compatibility.controller.js: -------------------------------------------------------------------------------- 1 | import ControllerExtension from "sap/ui/core/mvc/ControllerExtension"; 2 | 3 | export default class MyExtension extends ControllerExtension { 4 | 5 | static overrides = { 6 | onPageReady: function () { } 7 | } 8 | } -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-controller-extension-static-prop.controller.js: -------------------------------------------------------------------------------- 1 | import ControllerExtension from "sap/ui/core/mvc/ControllerExtension"; 2 | 3 | export default class MyExtension extends ControllerExtension { 4 | 5 | static overrides = { 6 | onPageReady: function () { } 7 | } 8 | } -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-controller-multi.controller.js: -------------------------------------------------------------------------------- 1 | // This fixture tests the detection of .controller.js files 2 | // and the option to auto-convert these controllers by default. 3 | 4 | import SAPController from "sap/ui/core/Controller"; 5 | import Other from "other"; 6 | 7 | class MyController extends SAPController {} 8 | 9 | class Controller2 extends SAPController {} 10 | 11 | /** 12 | * @nonui5 13 | */ 14 | class NotUI5 extends Other {} 15 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-controller-w-oninit.controller.js: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/Controller"; 2 | 3 | /** 4 | * @name test.fixtures.classes.MyController 5 | */ 6 | export default class MyController extends Controller { 7 | prop = 1; 8 | prop2 = this.a.b; 9 | prop3 = X.y; 10 | control = this.byId("control"); 11 | other = getThing(this.prop); 12 | /** 13 | * @keep false 14 | */ 15 | constructor(arg) { 16 | console.log("constructor"); 17 | super(arg); 18 | this.x = 1; 19 | } 20 | onInit() { 21 | this.y = this.x + 1; 22 | } 23 | prop_func = () => true; 24 | get getter() { 25 | return 1; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-controller-wo-oninit.controller.js: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/Controller"; 2 | 3 | /** 4 | * @name test.fixtures.classes.MyController 5 | */ 6 | export default class MyController extends Controller { 7 | /** 8 | * @keep false 9 | */ 10 | constructor(arg) { 11 | console.log("constructor"); 12 | super(arg); 13 | this.x = 1; 14 | } 15 | prop = 1; 16 | prop_func = () => true; 17 | get getter() { 18 | return 1; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-dec-alias.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/SAPClass"; 2 | 3 | @alias("x.y.Z") 4 | export default class MyClass extends SAPClass {} 5 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-dec-name.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/SAPClass"; 2 | 3 | @name("x.y.Z") 4 | export default class MyClass extends SAPClass {} 5 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-dec-namespace.controller.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/SAPClass"; 2 | 3 | @namespace("x.y") 4 | export default class MyClass extends SAPClass {} 5 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-dec-nonui5.js: -------------------------------------------------------------------------------- 1 | import NotSAPClass from "other/NotSAPClass"; 2 | 3 | @NONUI5 4 | export default class MyClass extends NotSAPClass {} 5 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-extend-managed.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/ui/core/SAPClass"; 2 | import Relative from "./Relative"; 3 | 4 | /** 5 | * @name test.fixtures.classes.MyClass 6 | */ 7 | export default class MyClass extends SAPClass { 8 | metadata = {}; 9 | renderer = {}; 10 | constructor(arg) { 11 | console.log("constructor"); 12 | super(arg); 13 | this.x = 1; 14 | } 15 | get prop() { 16 | return 1; 17 | } 18 | set prop(val) { 19 | this.x = val; 20 | } 21 | shorthandMethod(...args) { 22 | super.foo(...args); 23 | this.name = Relative.name(); 24 | } 25 | shorthandConflict() { 26 | // Make sure the shorthand function doesn"t a function with the same name. 27 | return shorthandConflict(); 28 | } 29 | async async_shorthand_function(arg) { 30 | return await 1; 31 | } 32 | property_value = 1; 33 | property_arrow_function = () => 1; 34 | static static_shorthand_function() { 35 | return 1; 36 | } 37 | static static_property_value = 1; 38 | static static_property_arrow = () => 1; 39 | static async static_async_shorthand_function() { 40 | return await 1; 41 | } 42 | } 43 | 44 | MyClass.z = function() { 45 | return "hey"; 46 | }; 47 | 48 | function shorthandConflict() { 49 | return 1; 50 | } 51 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-file-namespace-prefixed.controller.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/SAPClass"; 2 | 3 | /** 4 | * This test sets the namespace prefix (TODO: Implement prefix) 5 | * 6 | * @prefix xyz 7 | */ 8 | export default class MyClass extends SAPClass {} 9 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-file-namespace.controller.controller.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/SAPClass"; 2 | 3 | /** 4 | * The default behaviour is to use the filename to get the namespace. 5 | */ 6 | export default class MyClass extends SAPClass {} 7 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-jsdoc-alias.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/SAPClass"; 2 | 3 | /** 4 | * @alias x.y.Z 5 | */ 6 | export default class MyClass extends SAPClass {} 7 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-jsdoc-name.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/SAPClass"; 2 | 3 | /** 4 | * @name x.y.Z 5 | */ 6 | export default class MyClass extends SAPClass {} 7 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-jsdoc-namespace.controller.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/SAPClass"; 2 | 3 | /** 4 | * @namespace x.y 5 | */ 6 | export default class MyClass extends SAPClass {} 7 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-jsdoc-namespace.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/SAPClass"; 2 | 3 | /** 4 | * @namespace x.y 5 | * @controller 6 | */ 7 | export default class MyClass extends SAPClass {} 8 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-jsdoc-nonui5.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/SAPClass"; 2 | 3 | /** 4 | * @nonui5 5 | */ 6 | export default class MyClass extends SAPClass {} 7 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-metadata-assign-nested.js: -------------------------------------------------------------------------------- 1 | sap.ui.define(["sap/SAPClass"], SAPClass => { 2 | const Formatter = {}; 3 | const meta = {}; 4 | /** 5 | * @name x.y.MyClass 6 | */ 7 | class MyClass extends SAPClass {} 8 | MyClass.myFormatter = Formatter; 9 | MyClass.metadata = meta; 10 | MyClass.renderer = {}; 11 | return MyClass; 12 | }); 13 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-metadata-assign.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/SAPClass"; 2 | /** 3 | * @name test.fixtures.classes.MyClass 4 | */ 5 | export default class MyClass extends SAPClass {} 6 | const meta = {}; 7 | const Formatter = {}; 8 | /** 9 | * This tests finding the metadata after the class declaration. 10 | * This is how typescript compiles static props. 11 | */ 12 | MyClass.metadata = meta; 13 | MyClass.renderer = {}; 14 | MyClass.myFormatter = Formatter; 15 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-metadata-prop.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/SAPClass"; 2 | 3 | const Formatter = {}; 4 | const mdata = {}; 5 | 6 | /** 7 | * @name test.fixtures.classes.MyClass 8 | */ 9 | export default class MyClass extends SAPClass { 10 | metadata = mdata; 11 | myFormatter = Formatter; 12 | } 13 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-metadata-static.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/SAPClass"; 2 | 3 | const Formatter = Formatter; 4 | const mdata = {}; 5 | 6 | /** 7 | * @name test.fixtures.classes.MyClass 8 | */ 9 | export default class MyClass extends SAPClass { 10 | static metadata = mdata; 11 | static myFormatter = Formatter; 12 | } 13 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-never.controller.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This test uses config options to never convert. 3 | */ 4 | sap.ui.define(["other/OtherClass"], OtherClass => { 5 | return class MyClass extends OtherClass { 6 | constructor() { 7 | super(); 8 | } 9 | }; 10 | }); 11 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-no-constructor.js: -------------------------------------------------------------------------------- 1 | /* global sap */ 2 | /** 3 | * Test notes: Even though there is an existing sp.ui.define, 4 | * the class should be converted. 5 | */ 6 | sap.ui.define(["sap/SAPClass"], SAPClass => { 7 | /** 8 | * @name com.app.MyClass 9 | */ 10 | return class MyClass extends SAPClass { 11 | property_func = () => { 12 | return this.getProperty("/name"); 13 | }; 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-nonui5-return.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This fixture tests a nonui5 scenario inside an sap.ui.define. 3 | */ 4 | sap.ui.define(["sap/SAPClass"], SAPClass => { 5 | /** 6 | * @nonui5 7 | */ 8 | return class MyClass extends SAPClass {}; 9 | }); 10 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-only-move-this-binding.js: -------------------------------------------------------------------------------- 1 | import * as Util from "./lib/util"; 2 | import Controller from "sap/ui/core/mvc/Controller"; 3 | 4 | /** 5 | * @name app.AppController 6 | * @controller 7 | */ 8 | class AppController extends Controller { 9 | static metadata = { 10 | /* ... */ 11 | }; 12 | No = "not this"; 13 | No2 = Util.callback(function() { 14 | this.notThis(); 15 | }); 16 | control = this.byId("control"); 17 | controlCopy = this.control; 18 | 19 | boundSomething = this.something.bind(this); 20 | 21 | boundSomething2 = this.something.bind(null, this); 22 | boundSomething3 = Util.something.bind(this); 23 | 24 | arrowFunctionMember = () => { 25 | this.something(); 26 | }; 27 | arrowFunctionArgument = () => { 28 | Object.something(1, this); 29 | }; 30 | arrowFunctionNested = Util.debounce(100, () => { 31 | setTimeout(() => { 32 | this.something(); 33 | }); 34 | }); 35 | } 36 | 37 | export default AppController; 38 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-options-namedonly.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This test uses config options to only convert if there is a name provided. 3 | */ 4 | sap.ui.define(["other/OtherClass", "sap/SAPClass"], (OtherClass, SAPClass) => { 5 | class MyClass extends OtherClass { 6 | constructor() { 7 | super(); 8 | } 9 | } 10 | 11 | /** 12 | * @name my.app.MyUI5Class 13 | */ 14 | class MyUI5Class extends SAPClass { 15 | constructor() { 16 | super(); 17 | } 18 | } 19 | 20 | return { 21 | MyClass, 22 | MyUI5Class, 23 | }; 24 | }); 25 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-with-existing-define-defer-return.js: -------------------------------------------------------------------------------- 1 | /* global sap */ 2 | /** 3 | * Test notes: Even though there is an existing sp.ui.define, 4 | * the class should be converted. 5 | */ 6 | sap.ui.define(["sap/SAPClass"], SAPClass => { 7 | /** 8 | * @UI5 9 | * @name com.app.MyClass 10 | * @namespace com.app 11 | */ 12 | class MyClass extends SAPClass { 13 | static renderer = {}; 14 | static metadata = { 15 | thing: "string", 16 | }; 17 | constructor(data) { 18 | // This incorrect shorthand gets fixed 19 | super(data); 20 | } 21 | property_func = () => { 22 | return this.getProperty("/name"); 23 | }; 24 | } 25 | return MyClass; 26 | }); 27 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-convert-with-existing-define-immediate-return.js: -------------------------------------------------------------------------------- 1 | /* global sap */ 2 | /** 3 | * Test notes: Even though there is an existing sp.ui.define, 4 | * the class should be converted. 5 | */ 6 | sap.ui.define(["sap/SAPClass"], SAPClass => { 7 | /** 8 | * @UI5 9 | * @name com.app.MyClass 10 | * @namespace com.app 11 | */ 12 | return class MyClass extends SAPClass { 13 | static renderer = {}; 14 | static metadata = { 15 | thing: "string", 16 | }; 17 | constructor(data) { 18 | // This incorrect shorthand gets fixed 19 | super(data); 20 | } 21 | property_func = () => { 22 | return this.getProperty("/name"); 23 | }; 24 | }; 25 | }); 26 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-fix-with-existing-define.js: -------------------------------------------------------------------------------- 1 | /* global sap */ 2 | /** 3 | * Test notes: Even though there is an existing sp.ui.define, 4 | * and it uses .extend() syntax, the constructor function should be fixed. 5 | */ 6 | sap.ui.define(["sap/SAPClass"], SAPClass => { 7 | const X = SAPClass.extend("X", { 8 | // This incorrect shorthand will get fixed 9 | constructor(data) { 10 | SAPClass.prototype.constructor.call(this, data); 11 | }, 12 | method() { 13 | return this.getProperty("/name"); 14 | }, 15 | }); 16 | return X; 17 | }); 18 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classes/class-import-meta.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/SAPClass"; 2 | 3 | /** 4 | * @name test.fixtures.classes.MyClass 5 | */ 6 | export default class MyClass extends SAPClass { 7 | myUrl() { 8 | return import.meta.url; 9 | } 10 | resolveUrl(url) { 11 | return import.meta.resolve(url); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classic/sap-ui-define-copyright-file.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * ${copyright} 3 | */ 4 | 5 | // define class 6 | sap.ui.define("", ["foo/bar/MyResource"]); 7 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classic/sap-ui-define-copyright.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * ${copyright} 3 | */ 4 | sap.ui.define("", ["foo/bar/MyResource"]); 5 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/classic/sap-ui-define.ts: -------------------------------------------------------------------------------- 1 | sap.ui.define("", ["foo/bar/MyResource"]); 2 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/comments/copyright-file.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * ${copyright} 3 | */ 4 | 5 | // some imports 6 | import SAPClass, { Levels } from "sap/SAPClass"; 7 | 8 | /** 9 | * @name test.fixtures.libs.MyClass 10 | */ 11 | export default class MyClass extends SAPClass { 12 | static Levels = Levels; 13 | } 14 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/comments/copyright-multiple-comments.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * ${copyright} 3 | */ 4 | /** 5 | * Some other comment 6 | */ 7 | import SAPClass, { Levels } from "sap/SAPClass"; 8 | 9 | /** 10 | * @name test.fixtures.libs.MyClass 11 | */ 12 | export default class MyClass extends SAPClass { 13 | static Levels = Levels; 14 | } 15 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/comments/copyright.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * ${copyright} 3 | */ 4 | import SAPClass, { Levels } from "sap/SAPClass"; 5 | 6 | /** 7 | * @name test.fixtures.libs.MyClass 8 | */ 9 | export default class MyClass extends SAPClass { 10 | static Levels = Levels; 11 | } 12 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/comments/min-wrap-no-import-copyright.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * ${copyright} 3 | */ 4 | const x = 1; // This should be part of sap-ui-define 5 | 6 | export default x; 7 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/comments/min-wrap-simple-copyright.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * ${copyright} 3 | */ 4 | const x = 1; // This should not be part of sap-ui-define 5 | 6 | import Foo from "./foo"; 7 | 8 | const y = Foo(x); // This gets wrapped 9 | 10 | export default y; 11 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/comments/min-wrap-ui5-copyright.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * ${copyright} 3 | */ 4 | const x = 1; // This should not be part of sap-ui-define 5 | 6 | import Button from "sap/m/Button"; 7 | 8 | const b = new Button(); 9 | 10 | export default b; 11 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/comments/preserve-for-class-members.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/SAPClass"; 2 | 3 | const Formatter = Formatter; 4 | const mdata = {}; 5 | 6 | /** 7 | * @name test.fixtures.classes.MyClass 8 | */ 9 | export default class MyClass extends SAPClass { 10 | // some comment 11 | myNumber = 1; 12 | 13 | /** 14 | * block comment 15 | */ 16 | static myOtherNumber = 42; 17 | 18 | // this is MY method 19 | myMethod() { } 20 | 21 | /** 22 | * this is also my method 23 | */ 24 | myOtherMethod() { } 25 | 26 | // this property is not moved into the constructor 27 | overrides = {} 28 | } 29 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/decorators/mixin.js: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/mvc/Controller"; 2 | import { mixin } from "../lib/decorators"; 3 | import RoutingSupport from "../lib/RoutingSupport"; 4 | 5 | @mixin(RoutingSupport) 6 | export default class AppController extends Controller { 7 | /* ... */ 8 | } 9 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/decorators/mixin_mixed.js: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/mvc/Controller"; 2 | import { mixin } from "../lib/decorators"; 3 | import RoutingSupport from "../lib/RoutingSupport"; 4 | 5 | @namespace("example.controller") 6 | @mixin(RoutingSupport) 7 | export default class AppController extends Controller { 8 | /* ... */ 9 | } 10 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/decorators/ts-class-controller-extension-usage-decorator.ts: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/mvc/Controller"; 2 | import Routing from "sap/fe/core/controllerextensions/Routing"; 3 | 4 | /** 5 | * @namespace test.controller 6 | */ 7 | export default class MyExtendedController extends Controller { 8 | 9 | @transformControllerExtension 10 | routing: Routing; 11 | 12 | } -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/empty/empty_js.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ui5-community/babel-plugin-transform-modules-ui5/68d15e65c70e6dd5f35fc0fe47660131bb3d1a34/packages/plugin/__test__/fixtures/empty/empty_js.js -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/empty/empty_ts.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ui5-community/babel-plugin-transform-modules-ui5/68d15e65c70e6dd5f35fc0fe47660131bb3d1a34/packages/plugin/__test__/fixtures/empty/empty_ts.ts -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/examples/Animal.js: -------------------------------------------------------------------------------- 1 | import ManagedObject from "sap/ui/base/ManagedObject"; 2 | 3 | /** 4 | * @name examples.Animal 5 | */ 6 | export default class Animal extends ManagedObject { 7 | metadata = { 8 | properties: { 9 | type: { type: "string" }, 10 | nickName: { type: "string" }, 11 | }, 12 | }; 13 | 14 | constructor(...args) { 15 | super(...args); 16 | } 17 | 18 | init() { 19 | super.init(); 20 | } 21 | 22 | callMe() { 23 | alert(`I"m a ${this.getType()}. 24 | Call me ${this.getNickName()}.`); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/examples/Cat.js: -------------------------------------------------------------------------------- 1 | import Animal from "./Animal"; 2 | 3 | /** 4 | * @name othernamespace.Cat 5 | */ 6 | export default class Cat extends Animal { 7 | init() { 8 | super.init(); 9 | this.setType("Cat"); 10 | } 11 | 12 | callMe() { 13 | super.callMe(); 14 | alert("Miao~"); 15 | } 16 | 17 | static createCat(nickName) { 18 | const cat = new example.obj.Cat({ 19 | nickName, 20 | }); 21 | return cat; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/examples/MyError.js: -------------------------------------------------------------------------------- 1 | export default class MyError extends Error { 2 | constructor(msg) { 3 | super(msg); 4 | this.name = "MyError"; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/examples/Utils.js: -------------------------------------------------------------------------------- 1 | export function multiply(a, b) { 2 | return a * b; 3 | } 4 | 5 | function add(a, b) { 6 | // Not a named export but gets added for sap.ui.define() interop. 7 | return a + b; 8 | } 9 | 10 | export default { 11 | multiply, 12 | add, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/export-collapse/_extends-literal.js: -------------------------------------------------------------------------------- 1 | /* 2 | Test the plugin"s ability to find properties of the default export: 3 | - from inside _extend function (used by TS and babel) 4 | - including properties from another variable 5 | - when the _extends result is exported directly 6 | */ 7 | 8 | import X from "x"; 9 | 10 | export const one = 1; 11 | export function two() {} 12 | export const three = 3; 13 | 14 | const O2 = { 15 | three, 16 | }; 17 | 18 | export default _extends({}, X, O2, { 19 | one, 20 | two, 21 | }); 22 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/export-collapse/_extends-variable.js: -------------------------------------------------------------------------------- 1 | /* 2 | Tests the plugin"s ability to find properties of the default export: 3 | - when the default export is a variable. 4 | - from dot-assigned properties 5 | - from inside _extend function (used by TS and babel) 6 | - including properties from another variable 7 | */ 8 | 9 | import X from "x"; 10 | 11 | export const one = 1; 12 | export function two() {} 13 | export function three() {} 14 | export function four() {} 15 | 16 | const O2 = { 17 | three, 18 | }; 19 | 20 | let D; 21 | 22 | D = _extends({}, X, O2, { 23 | one, 24 | two, 25 | }); 26 | 27 | D.four = four; 28 | 29 | export default D; 30 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/export-collapse/add-props-obj.js: -------------------------------------------------------------------------------- 1 | /** 2 | Tests that exports can be collapsed onto to an object using a variable. 3 | - Named export "one" can be ignored since it"s on X and as the same literal. 4 | - Named export "foo" can be ignored since it"s on X and as the same literal. 5 | - Named export "two" gets assigned to "X" 6 | - Named export "bar" gets assigned to "X" 7 | */ 8 | 9 | export function one() {} 10 | export function two() {} 11 | 12 | export { one as foo, two as bar }; 13 | 14 | const X = { 15 | one, 16 | foo: one, 17 | }; 18 | 19 | export default X; 20 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/export-collapse/object.assign-literal.js: -------------------------------------------------------------------------------- 1 | import X from "x"; 2 | 3 | export const one = 1; 4 | export const two = "2"; 5 | export function foo() {} 6 | 7 | export const anotherN = 0; 8 | export const anotherS = "s"; 9 | 10 | export default Object.assign({}, X, { 11 | one: 1, 12 | two: "2", 13 | foo, 14 | }); 15 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/export-collapse/object.assign-variable.js: -------------------------------------------------------------------------------- 1 | import X from "x"; 2 | 3 | export const one = 1; 4 | export function two() {} 5 | export const three = 3; 6 | export function five() {} 7 | export function six() {} 8 | 9 | export const anotherConst = 9; 10 | export function otherFN() {} 11 | 12 | const O2 = { 13 | six, 14 | }; 15 | 16 | const Utils = Object.assign( 17 | {}, 18 | X, 19 | { 20 | one, 21 | two, 22 | }, 23 | { 24 | five, 25 | }, 26 | O2 // recursion test 27 | ); 28 | 29 | Utils.three = 3; 30 | 31 | export default Utils; 32 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/export-error/error-assign-var-recurs.js: -------------------------------------------------------------------------------- 1 | export function foo() {} // conflicts with O2 foo 2 | 3 | const O2 = { 4 | foo: 1, 5 | }; 6 | 7 | const Exported = Object.assign( 8 | {}, 9 | O2 // recursion test 10 | ); 11 | 12 | export default Exported; 13 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/export-error/error-assign-var.js: -------------------------------------------------------------------------------- 1 | import X from "x"; 2 | 3 | export function foo() {} // conflicts with Exported.foo 4 | 5 | const O2 = { 6 | six, 7 | }; 8 | 9 | const Exported = Object.assign({}, X); 10 | 11 | Exported.foo = 1; 12 | 13 | export default Exported; 14 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/export-error/error-obj-method.js: -------------------------------------------------------------------------------- 1 | /* 2 | This tests an error because the definitions of fn collide. 3 | */ 4 | 5 | export function a() { 6 | return "X"; 7 | } 8 | 9 | export default { 10 | a() { 11 | // conflicting definition from the named export a 12 | return "Y"; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/export-error/error-obj-prop.js: -------------------------------------------------------------------------------- 1 | // This tests the scenario when there is a mix of named and default exports. 2 | // If the importing module has an interop, there"s no issue with that. 3 | // But if not, the importing module will get the "exports" object and not the "default" object. 4 | // If the "exports" doesn"t have all the properties that "default" has, the importing module will get an unexpected undefined. 5 | 6 | export function a() { 7 | return "a"; 8 | } 9 | 10 | function b() { 11 | return "b"; 12 | } 13 | 14 | export default { 15 | a: b, // conflicting definition from the named export a 16 | }; 17 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/export-mixed/export-mixed-anon-func.js: -------------------------------------------------------------------------------- 1 | export default function() { 2 | console.log("I am the default"); 3 | } 4 | export const named = {}; 5 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/export-mixed/export-mixed-arrow.js: -------------------------------------------------------------------------------- 1 | export default () => { 2 | return 1; 3 | }; 4 | 5 | export const two = 2; 6 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/export-mixed/export-mixed-literal.js: -------------------------------------------------------------------------------- 1 | export const a = 1; 2 | export const b = 2; 3 | 4 | export { a as x }; 5 | export { b as y }; 6 | 7 | export default { 8 | a, 9 | x: a, 10 | }; 11 | 12 | export const c = 3; 13 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/export-mixed/export-mixed-named-func.js: -------------------------------------------------------------------------------- 1 | export default function def() { 2 | console.log("I am the default"); 3 | } 4 | def.hey = "hey"; 5 | export { def }; // TODO: detect the self reference and don"t extend 6 | export const named = {}; 7 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/exports/export-class-extend-native.js: -------------------------------------------------------------------------------- 1 | export default class MyError extends Error { 2 | constructor(m) { 3 | super(m); 4 | console.log("constructor"); 5 | this.name = "MyError"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/exports/export-class.js: -------------------------------------------------------------------------------- 1 | function hello() { 2 | return "hey"; 3 | } 4 | 5 | export default class Thing { 6 | constructor() { 7 | console.log("constructor"); 8 | } 9 | } 10 | 11 | Thing.foo = hello; 12 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/exports/export-default-anon-function.js: -------------------------------------------------------------------------------- 1 | let one = 1; 2 | export default async function() { 3 | // Since it"s anonymous, this can be safely moved to the end 4 | return (await one) + two; 5 | } 6 | let two = 2; 7 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/exports/export-default-arrow-function.js: -------------------------------------------------------------------------------- 1 | let one = 1; 2 | export default () => { 3 | return one + two; 4 | }; 5 | let two = 2; 6 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/exports/export-default-identifier.js: -------------------------------------------------------------------------------- 1 | const x = {}; 2 | 3 | export default x; 4 | 5 | x.a = 1; 6 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/exports/export-default-named-function.js: -------------------------------------------------------------------------------- 1 | let one = 1; 2 | 3 | export default function add() { 4 | return one + two + this.x; 5 | } 6 | 7 | add.x = 3; 8 | 9 | let two = 2; 10 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/exports/export-multiple.js: -------------------------------------------------------------------------------- 1 | const thing1 = {}; 2 | const thing2 = 2; 3 | const thingy3 = 3; 4 | 5 | export { thing1, thing2, thingy3 as thing3 }; 6 | 7 | export const c = 1; 8 | export var v = 1; 9 | export let l = 1; 10 | export function f() {} 11 | 12 | export let laterLet; 13 | laterLet = "now"; 14 | 15 | export var laterVar; 16 | laterVar = "var"; 17 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/exports/export-named-as-default.js: -------------------------------------------------------------------------------- 1 | const one = 1; 2 | export { one as default }; 3 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/exports/export-named-from.js: -------------------------------------------------------------------------------- 1 | export { A, B } from "A"; 2 | export { C, D } from "path/having-dash"; 3 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/exports/export-obj.js: -------------------------------------------------------------------------------- 1 | // Hello World 2 | 3 | function getFoo() { 4 | return 1; 5 | } 6 | 7 | function bar() { 8 | return 2; 9 | } 10 | 11 | export default { 12 | foo: getFoo(), 13 | bar, 14 | }; 15 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/flow/flow-exports.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import type FooType, { OtherType } from "types"; 4 | 5 | import SAPClass from "sap/SAPClass"; 6 | 7 | /** 8 | * @name Foo 9 | */ 10 | export default class Foo extends SAPClass { 11 | prop: number; 12 | } 13 | 14 | // let fooInstance: Foo = new Foo(); 15 | // export fooInstance; 16 | 17 | export type MyObject = { 18 | /* ... */ 19 | }; 20 | 21 | export interface MyInterface { 22 | /* ... */ 23 | } 24 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/imports/_private_/module_js.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ui5-community/babel-plugin-transform-modules-ui5/68d15e65c70e6dd5f35fc0fe47660131bb3d1a34/packages/plugin/__test__/fixtures/imports/_private_/module_js.js -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/imports/_private_/module_jsx.jsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ui5-community/babel-plugin-transform-modules-ui5/68d15e65c70e6dd5f35fc0fe47660131bb3d1a34/packages/plugin/__test__/fixtures/imports/_private_/module_jsx.jsx -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/imports/_private_/module_ts.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ui5-community/babel-plugin-transform-modules-ui5/68d15e65c70e6dd5f35fc0fe47660131bb3d1a34/packages/plugin/__test__/fixtures/imports/_private_/module_ts.ts -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/imports/_private_/module_tsx.tsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ui5-community/babel-plugin-transform-modules-ui5/68d15e65c70e6dd5f35fc0fe47660131bb3d1a34/packages/plugin/__test__/fixtures/imports/_private_/module_tsx.tsx -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/imports/import-all.js: -------------------------------------------------------------------------------- 1 | import * as G from "G"; 2 | 3 | export default G; 4 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/imports/import-at-sign.js: -------------------------------------------------------------------------------- 1 | import "@babel/polyfill"; 2 | import "dash-separated"; 3 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/imports/import-duplicate-src-1.js: -------------------------------------------------------------------------------- 1 | // namespace line first 2 | import * as XA from "X"; 3 | import XD, { X2 } from "X"; 4 | import { X3 } from "X"; 5 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/imports/import-duplicate-src-2.js: -------------------------------------------------------------------------------- 1 | // default line first 2 | import XD, { X2 } from "X"; 3 | import * as XA from "X"; 4 | import { X3 } from "X"; 5 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/imports/import-duplicate-src-3.js: -------------------------------------------------------------------------------- 1 | // default line first, mixed with namespace 2 | import XD, * as XA from "X"; 3 | import { X2, X3 } from "X"; 4 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/imports/import-dynamic.js: -------------------------------------------------------------------------------- 1 | async function getItAA() { 2 | const SAPModule = await import("sap/SAPModule"); 3 | return SAPModule.default; 4 | } 5 | 6 | function getItThen() { 7 | return import("my/Module").then(MyModule => { 8 | return MyModule.default; 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/imports/import-modules-map-fn.js: -------------------------------------------------------------------------------- 1 | import "@babel/polyfill"; 2 | import X, { Named1 } from "@babel/polyfill"; 3 | import { Named2 } from "@babel/polyfill"; 4 | import * as X2 from "@babel/polyfill"; 5 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/imports/import-modules-map.js: -------------------------------------------------------------------------------- 1 | import "@babel/polyfill"; 2 | import X, { Named1 } from "@babel/polyfill"; 3 | import { Named2 } from "@babel/polyfill"; 4 | import * as X2 from "@babel/polyfill"; 5 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/imports/import-multiple.js: -------------------------------------------------------------------------------- 1 | import "./../a/b"; 2 | import "path/having-dash"; 3 | import A from "A"; 4 | import { B, C } from "BC"; 5 | import D, { E, F } from "DEF"; 6 | import * as G from "G"; 7 | import H, * as I from "HI"; 8 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/imports/import-remove-fileext.js: -------------------------------------------------------------------------------- 1 | import "@dummy/chart.js"; 2 | import "chart.js"; 3 | import "array-flatten/dist/index.js"; 4 | import "./_private_/module_js.js"; 5 | import "./_private_/module_jsx.js"; 6 | import "./_private_/module_ts.js"; 7 | import "./_private_/module_tsx.js"; 8 | import "./_private_/unknown_module.js"; 9 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/libs/libs-other-imports.js: -------------------------------------------------------------------------------- 1 | import SAPClass, { Levels } from "sap/SAPClass"; 2 | import _ from "lodash"; 3 | 4 | /** 5 | * @name test.fixtures.libs.MyClass 6 | */ 7 | export default class MyClass extends SAPClass { 8 | static Levels = Levels; 9 | } 10 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/libs/libs-sap-imports-dashes.js: -------------------------------------------------------------------------------- 1 | import SAPClassDashes, { Levels } from "sap/SAPClass-with-dashes"; 2 | 3 | /** 4 | * @name test.fixtures.libs.MyClass 5 | */ 6 | export default class MyClass extends SAPClassDashes { 7 | static Levels = Levels; 8 | } 9 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/libs/libs-sap-imports.js: -------------------------------------------------------------------------------- 1 | import SAPClass, { Levels } from "sap/SAPClass"; 2 | 3 | /** 4 | * @name test.fixtures.libs.MyClass 5 | */ 6 | export default class MyClass extends SAPClass { 7 | static Levels = Levels; 8 | } 9 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/min-wrap/min-wrap-no-import.js: -------------------------------------------------------------------------------- 1 | const x = 1; // This should be part of sap-ui-define 2 | 3 | export default x; 4 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/min-wrap/min-wrap-simple.js: -------------------------------------------------------------------------------- 1 | const x = 1; // This should not be part of sap-ui-define 2 | 3 | import Foo from "./foo"; 4 | 5 | const y = Foo(x); // This gets wrapped 6 | 7 | export default y; 8 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/min-wrap/min-wrap-test-wrap.js: -------------------------------------------------------------------------------- 1 | const x = 1; // This should not be part of sap-ui-define 2 | 3 | import { getElementPath } from "../../model/someFile"; 4 | import { createSchema } from "../schema/otherFile"; 5 | 6 | getElementPath(createSchema(), "elementSchema"); 7 | console.log(x); 8 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/min-wrap/min-wrap-test.js: -------------------------------------------------------------------------------- 1 | import { getElementPath } from "../../model/someFile"; 2 | import { createSchema } from "../schema/otherFile"; 3 | 4 | getElementPath(createSchema(), "elementSchema"); 5 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/min-wrap/min-wrap-ui5.js: -------------------------------------------------------------------------------- 1 | const x = 1; // This should not be part of sap-ui-define 2 | 3 | import Button from "sap/m/Button"; 4 | 5 | const b = new Button(); 6 | 7 | export default b; 8 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/mixed/mixed-import-export.js: -------------------------------------------------------------------------------- 1 | export * from "1"; 2 | export { a, b as b2 } from "2"; 3 | export { default } from "3"; 4 | export { default as default2 } from "4"; 5 | export { v as x } from "5"; 6 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/never-use-strict/use-strict-define.js: -------------------------------------------------------------------------------- 1 | import Button from "sap/m/Button"; 2 | 3 | const b = new Button(); 4 | 5 | export default b; 6 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/never-use-strict/use-strict-program.js: -------------------------------------------------------------------------------- 1 | const x = 1; // This should not be part of sap-ui-define 2 | 3 | import Button from "sap/m/Button"; 4 | 5 | const b = new Button(); 6 | 7 | export default b; 8 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/no-wrap/nowrap-existing-sap.ui.define.js: -------------------------------------------------------------------------------- 1 | /* global sap */ 2 | sap.ui.define(["JSONModel"], JSONModel => { 3 | return new JSONModel(); 4 | }); 5 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/no-wrap/skip-iffe.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | window.x = {}; 3 | })(); 4 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/preset-env/preset-env-class.js: -------------------------------------------------------------------------------- 1 | // This conversion will be run with preset env set to ie >= 11. 2 | // In general, we want to ensure that the class transform does not get applied before out own. 3 | 4 | import SAPClass from "sap/class"; 5 | 6 | /** 7 | * @name my.MyClass 8 | */ 9 | export default class MyClass extends SAPClass { 10 | x = 1; 11 | 12 | static X = 1; 13 | 14 | fn() { 15 | // The purpose of the setTimeout is to ensure that "this" is correct for the super call 16 | // when the arrow function transform is applied (i.e. our super transform must run first) 17 | setTimeout(() => { 18 | super.fn(); 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/preset-env/preset-env-property-mutators.js: -------------------------------------------------------------------------------- 1 | // This conversion will be run with preset env set to ie >= 11. 2 | // In general, we want to ensure that the class transform does not get applied before out own. 3 | 4 | import SAPClass from "sap/class"; 5 | 6 | /** 7 | * @name my.MyClass 8 | */ 9 | export default class MyClass extends SAPClass { 10 | get thing() { 11 | return this._thing; 12 | } 13 | set thing(value) { 14 | this._thing = value; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/preset-env/preset-env-usage.js: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/SAPClass"; 2 | 3 | /** 4 | * @name my.MyClass 5 | */ 6 | export default class AClass extends SAPClass { 7 | delay() { 8 | return new Promise(resolve => { 9 | setTimeout(resolve); 10 | }); 11 | } 12 | includes(str) { 13 | return str.includes("thing"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/sap-ui-require/othermodule-annotation-end.js: -------------------------------------------------------------------------------- 1 | import Control from "sap/ui/core/Control"; 2 | 3 | Control.extend("my.Control", {}); 4 | 5 | /* @sapUiRequire */ 6 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/sap-ui-require/othermodule-annotation-middle.js: -------------------------------------------------------------------------------- 1 | import Control from "sap/ui/core/Control"; 2 | 3 | /* @sapUiRequire */ 4 | 5 | Control.extend("my.Control", {}); 6 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/sap-ui-require/othermodule-annotation-nested.js: -------------------------------------------------------------------------------- 1 | import Control from "sap/ui/core/Control"; 2 | 3 | Control.extend("my.Control", { 4 | onInit: function () { 5 | /* @sapUiRequire */ 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/sap-ui-require/othermodule-annotation.js: -------------------------------------------------------------------------------- 1 | /* @sapUiRequire */ 2 | 3 | import Control from "sap/ui/core/Control"; 4 | 5 | Control.extend("my.Control", {}); 6 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/sap-ui-require/othermodule-noannotation.js: -------------------------------------------------------------------------------- 1 | import Control from "sap/ui/core/Control"; 2 | 3 | Control.extend("my.Control", {}); 4 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/sap-ui-require/testsuite-annotation-with-qunit-import.qunit.js: -------------------------------------------------------------------------------- 1 | /* @sapUiRequire */ 2 | import QUnit from "qunit"; 3 | 4 | // https://api.qunitjs.com/config/autostart/ 5 | QUnit.config.autostart = false; 6 | 7 | // import all your QUnit tests here 8 | void Promise.all([import("unit/controller/App.qunit")]).then(() => { 9 | QUnit.start(); 10 | }); 11 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/sap-ui-require/testsuite-annotation.qunit.js: -------------------------------------------------------------------------------- 1 | /* @sapUiRequire */ 2 | 3 | // https://api.qunitjs.com/config/autostart/ 4 | QUnit.config.autostart = false; 5 | 6 | // import all your QUnit tests here 7 | void Promise.all([import("unit/controller/App.qunit")]).then(() => { 8 | QUnit.start(); 9 | }); 10 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/sap-ui-require/testsuite-noannotation.qunit.js: -------------------------------------------------------------------------------- 1 | // https://api.qunitjs.com/config/autostart/ 2 | QUnit.config.autostart = false; 3 | 4 | // import all your QUnit tests here 5 | void Promise.all([import("unit/controller/App.qunit")]).then(() => { 6 | QUnit.start(); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript-preset-env/ts-class-anonymous-copyright-file.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * ${copyright} 3 | */ 4 | 5 | // some imports 6 | import SAPClass from "sap/Class"; 7 | import IClipboardContent from "sap/IClipboardContent"; 8 | 9 | /** 10 | * @name MyClass 11 | */ 12 | export default class MyClass extends SAPClass { 13 | createAnonymousClass() { 14 | return new (class implements IClipboardContent { 15 | getClipboardContentType() { 16 | /* ... */ 17 | } 18 | })(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript-preset-env/ts-class-anonymous-copyright.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * ${copyright} 3 | */ 4 | import SAPClass from "sap/Class"; 5 | import IClipboardContent from "sap/IClipboardContent"; 6 | 7 | /** 8 | * @name MyClass 9 | */ 10 | export default class MyClass extends SAPClass { 11 | createAnonymousClass() { 12 | return new (class implements IClipboardContent { 13 | getClipboardContentType() { 14 | /* ... */ 15 | } 16 | })(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript-preset-env/ts-class-anonymous.ts: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/Class"; 2 | import IClipboardContent from "sap/IClipboardContent"; 3 | 4 | /** 5 | * @name MyClass 6 | */ 7 | export default class MyClass extends SAPClass { 8 | createAnonymousClass() { 9 | return new (class implements IClipboardContent { 10 | getClipboardContentType() { 11 | /* ... */ 12 | } 13 | })(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript-preset-env/ts-class-param-props-copyright-file.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * ${copyright} 3 | */ 4 | 5 | // some imports 6 | import SAPClass from "sap/Class"; 7 | import SAPBar from "sap/Bar"; 8 | import SAPFoo from "sap/Foo"; 9 | 10 | /** 11 | * @name MyClass 12 | */ 13 | export default class MyClass extends SAPClass { 14 | foo: SAPFoo; 15 | 16 | constructor(public bar: SAPBar, private x: string, readonly y: string) { 17 | super(); 18 | this.foo = bar.getFoo(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript-preset-env/ts-class-param-props-copyright.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * ${copyright} 3 | */ 4 | import SAPClass from "sap/Class"; 5 | import SAPBar from "sap/Bar"; 6 | import SAPFoo from "sap/Foo"; 7 | 8 | /** 9 | * @name MyClass 10 | */ 11 | export default class MyClass extends SAPClass { 12 | foo: SAPFoo; 13 | 14 | constructor(public bar: SAPBar, private x: string, readonly y: string) { 15 | super(); 16 | this.foo = bar.getFoo(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript-preset-env/ts-class-param-props.ts: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/Class"; 2 | import SAPBar from "sap/Bar"; 3 | import SAPFoo from "sap/Foo"; 4 | 5 | /** 6 | * @name MyClass 7 | */ 8 | export default class MyClass extends SAPClass { 9 | foo: SAPFoo; 10 | 11 | constructor(public bar: SAPBar, private x: string, readonly y: string) { 12 | super(); 13 | this.foo = bar.getFoo(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/_private_/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./type"; 2 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/_private_/type.ts: -------------------------------------------------------------------------------- 1 | export type StringOrBoolean = string | boolean; 2 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-class-anonymous-copyright-file.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * ${copyright} 3 | */ 4 | 5 | // some imports 6 | import SAPClass from "sap/Class"; 7 | import IClipboardContent from "sap/IClipboardContent"; 8 | 9 | /** 10 | * @name MyClass 11 | */ 12 | export default class MyClass extends SAPClass { 13 | createAnonymousClass() { 14 | return new (class implements IClipboardContent { 15 | getClipboardContentType() { 16 | /* ... */ 17 | } 18 | })(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-class-anonymous-copyright.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * ${copyright} 3 | */ 4 | import SAPClass from "sap/Class"; 5 | import IClipboardContent from "sap/IClipboardContent"; 6 | 7 | /** 8 | * @name MyClass 9 | */ 10 | export default class MyClass extends SAPClass { 11 | createAnonymousClass() { 12 | return new (class implements IClipboardContent { 13 | getClipboardContentType() { 14 | /* ... */ 15 | } 16 | })(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-class-anonymous.ts: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/Class"; 2 | import IClipboardContent from "sap/IClipboardContent"; 3 | 4 | /** 5 | * @name MyClass 6 | */ 7 | export default class MyClass extends SAPClass { 8 | createAnonymousClass() { 9 | return new (class implements IClipboardContent { 10 | getClipboardContentType() { 11 | /* ... */ 12 | } 13 | })(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-class-controller-extension-extended-error-1.ts: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/mvc/Controller"; 2 | import ControllerExtension from "sap/ui/core/mvc/ControllerExtension"; 3 | import Routing from "sap/fe/core/controllerextensions/Routing"; 4 | 5 | /** 6 | * @namespace test.controller 7 | */ 8 | export default class MyExtendedController extends Controller { 9 | routing = ControllerExtension.use(); // should throw an error 10 | } -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-class-controller-extension-extended-error-2.ts: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/mvc/Controller"; 2 | import ControllerExtension from "sap/ui/core/mvc/ControllerExtension"; 3 | import Routing from "sap/fe/core/controllerextensions/Routing"; 4 | 5 | /** 6 | * @namespace test.controller 7 | */ 8 | export default class MyExtendedController extends Controller { 9 | routing = ControllerExtension.use(1, 2); // should throw an error 10 | } -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-class-controller-extension-extended-explicit-type.ts: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/mvc/Controller"; 2 | import ControllerExtension from "sap/ui/core/mvc/ControllerExtension"; 3 | import Routing from "sap/fe/core/controllerextensions/Routing"; 4 | 5 | /** 6 | * @namespace test.controller 7 | */ 8 | export default class MyExtendedController extends Controller { 9 | routing: Routing = ControllerExtension.use(Routing.override({})); 10 | routing2: Routing = ControllerExtension.use(Routing.override({ 11 | x: 1, 12 | fn: () => { } 13 | })); 14 | } -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-class-controller-extension-extended-renamed.ts: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/mvc/Controller"; 2 | import CtrlEx from "sap/ui/core/mvc/ControllerExtension"; 3 | import Routing from "sap/fe/core/controllerextensions/Routing"; 4 | 5 | /** 6 | * @namespace test.controller 7 | */ 8 | export default class MyExtendedController extends Controller { 9 | routing = /* comment */ CtrlEx.use( 10 | /* comment */ 11 | Routing.override( 12 | /* comment */ 13 | { 14 | /* comment */ 15 | } 16 | )); 17 | } -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-class-controller-extension-extended.ts: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/mvc/Controller"; 2 | import ControllerExtension from "sap/ui/core/mvc/ControllerExtension"; 3 | import Routing from "sap/fe/core/controllerextensions/Routing"; 4 | 5 | /** 6 | * @namespace test.controller 7 | */ 8 | export default class MyExtendedController extends Controller { 9 | routing = ControllerExtension.use(Routing.override({})); 10 | routing2 = (Routing as any).use(Routing.override({})); // should not even try to handle this 11 | routing3 = Controller.use(Routing.override({})); // should not even try to handle this 12 | } -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-class-controller-extension-usage-new.ts: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/mvc/Controller"; 2 | import Routing from "sap/fe/core/controllerextensions/Routing"; 3 | import { OtherExtension } from "sap/fe/core/controllerextensions/OtherExtension"; 4 | import * as ThirdExtension from "sap/fe/core/controllerextensions/ThirdExtension"; 5 | import { SomethingElse, AlmostRemovedExtension } from "sap/fe/core/controllerextensions/DoubleExportExtension"; 6 | import * as extensionCollection from "sap/fe/core/controllerextensions/ManyExtensions"; 7 | import ControllerExtension from "sap/ui/core/mvc/ControllerExtension"; 8 | 9 | /** 10 | * @namespace test.controller 11 | */ 12 | export default class MyExtendedController extends Controller { 13 | 14 | routing = ControllerExtension.use(Routing); 15 | 16 | other = ControllerExtension.use(OtherExtension); 17 | 18 | third = ControllerExtension.use(ThirdExtension); 19 | 20 | fourth = ControllerExtension.use(AlmostRemovedExtension); 21 | 22 | fifth = ControllerExtension.use(extensionCollection.group.OneOfManyExtensions); 23 | 24 | } -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-class-controller-extension-usage.ts: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/mvc/Controller"; 2 | import Routing from "sap/fe/core/controllerextensions/Routing"; 3 | import { OtherExtension } from "sap/fe/core/controllerextensions/OtherExtension"; 4 | import * as ThirdExtension from "sap/fe/core/controllerextensions/ThirdExtension"; 5 | import { SomethingElse, AlmostRemovedExtension } from "sap/fe/core/controllerextensions/DoubleExportExtension"; 6 | import * as extensionCollection from "sap/fe/core/controllerextensions/ManyExtensions"; 7 | 8 | /** 9 | * @namespace test.controller 10 | */ 11 | export default class MyExtendedController extends Controller { 12 | 13 | /** 14 | * @transformControllerExtension 15 | */ 16 | routing: Routing; 17 | 18 | // @transformControllerExtension 19 | other: OtherExtension; 20 | 21 | // @transformControllerExtension 22 | third: ThirdExtension; 23 | 24 | // @transformControllerExtension 25 | fourth: AlmostRemovedExtension; 26 | 27 | // @transformControllerExtension 28 | fifth: extensionCollection.group.OneOfManyExtensions; 29 | 30 | realPropertyExtension; 31 | 32 | constructor() { 33 | super(); 34 | 35 | this.realPropertyExtension = SomethingElse.Something; 36 | } 37 | } -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-class-controller-extension-wrapped.ts: -------------------------------------------------------------------------------- 1 | import Controller from "sap/ui/core/mvc/Controller"; 2 | import ControllerExtension from "sap/ui/core/mvc/ControllerExtension"; 3 | import Routing from "sap/fe/core/controllerextensions/Routing"; 4 | 5 | const cov_1uvvg22e7l = () => { return { "s": {} }; }; // dummy coverage function 6 | 7 | /** 8 | * @namespace test.controller 9 | */ 10 | export default class MyExtendedController extends Controller { 11 | 12 | // code could already be instrumented, e.g. for code coverage by istanbul, and look like this: 13 | //this.routing = (cov_1uvvg22e7l().s[5]++, ControllerExtension.use(Routing.override({ … }))); 14 | routing = (cov_1uvvg22e7l().s[5]++, ControllerExtension.use(Routing)); 15 | routing2 = (cov_1uvvg22e7l().s[5]++, ControllerExtension.use(Routing.override({}))); 16 | routing3 = (cov_1uvvg22e7l().s[5]++, cov_1uvvg22e7l().s[5]++, ControllerExtension.use(Routing.override({}))); 17 | routing4 = (cov_1uvvg22e7l().s[5]++, ControllerExtension.use(Routing.override({})), ControllerExtension.use(Routing)); 18 | } -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-class-param-props-copyright-file.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * ${copyright} 3 | */ 4 | 5 | // some imports 6 | import SAPClass from "sap/Class"; 7 | import SAPBar from "sap/Bar"; 8 | import SAPFoo from "sap/Foo"; 9 | 10 | /** 11 | * @name MyClass 12 | */ 13 | export default class MyClass extends SAPClass { 14 | foo: SAPFoo; 15 | 16 | constructor(public bar: SAPBar, private x: string, readonly y: string) { 17 | super(); 18 | this.foo = bar.getFoo(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-class-param-props-copyright.ts: -------------------------------------------------------------------------------- 1 | /*! 2 | * ${copyright} 3 | */ 4 | import SAPClass from "sap/Class"; 5 | import SAPBar from "sap/Bar"; 6 | import SAPFoo from "sap/Foo"; 7 | 8 | /** 9 | * @name MyClass 10 | */ 11 | export default class MyClass extends SAPClass { 12 | foo: SAPFoo; 13 | 14 | constructor(public bar: SAPBar, private x: string, readonly y: string) { 15 | super(); 16 | this.foo = bar.getFoo(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-class-param-props.ts: -------------------------------------------------------------------------------- 1 | import SAPClass from "sap/Class"; 2 | import SAPBar from "sap/Bar"; 3 | import SAPFoo from "sap/Foo"; 4 | 5 | /** 6 | * @name MyClass 7 | */ 8 | export default class MyClass extends SAPClass { 9 | foo: SAPFoo; 10 | 11 | constructor(public bar: SAPBar, private x: string, readonly y: string) { 12 | super(); 13 | this.foo = bar.getFoo(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-class-props-only-move-this.ts: -------------------------------------------------------------------------------- 1 | 2 | import SAPClass from "sap/Class"; 3 | 4 | /** 5 | * @name MyClass 6 | */ 7 | class MyClass extends SAPClass { 8 | S: string = "S"; 9 | private PS: string = "PS"; 10 | private readonly PRS: string = "PRS"; 11 | 12 | static SS: string = "SS"; 13 | private static PSS: string = "PSS"; 14 | private static readonly PSRS: string = "PSRS"; 15 | 16 | // The following shouldn't be added as members 17 | X: any; 18 | private PX: any; 19 | private readonly PRX; 20 | 21 | static SX: string; 22 | private static PSX: any; 23 | private static readonly PSRX: any; 24 | } 25 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-class-props.ts: -------------------------------------------------------------------------------- 1 | 2 | import SAPClass from "sap/Class"; 3 | 4 | /** 5 | * @name MyClass 6 | */ 7 | class MyClass extends SAPClass { 8 | S: string = "S"; 9 | private PS: string = "PS"; 10 | private readonly PRS: string = "PRS"; 11 | 12 | static SS: string = "SS"; 13 | private static PSS: string = "PSS"; 14 | private static readonly PSRS: string = "PSRS"; 15 | 16 | // The following shouldn't be added as members 17 | X: any; 18 | private PX: any; 19 | private readonly PRX; 20 | 21 | static SX: string; 22 | private static PSX: any; 23 | private static readonly PSRX: any; 24 | } 25 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-export-default-typed-anon-fn-only-default.ts: -------------------------------------------------------------------------------- 1 | export default function(date: Date): string { 2 | return ""; 3 | } 4 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-export-default-typed-anon-obj.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | x: 1 as any, 3 | } as any; 4 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-export-default-typed-arrow-fn-only-default.ts: -------------------------------------------------------------------------------- 1 | const fn = (date: Date): string => { 2 | return ""; 3 | }; 4 | 5 | export default (date: Date): string => { 6 | return ""; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-export-default-typed-arrow-fn-with-named.1.ts: -------------------------------------------------------------------------------- 1 | const fn = (date: Date): string => { 2 | return ""; 3 | }; 4 | 5 | export { fn }; 6 | 7 | export default (date: Date): string => { 8 | return ""; 9 | }; 10 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-export-interface.ts: -------------------------------------------------------------------------------- 1 | 2 | export const MY_CONSTANT = "constant"; 3 | export interface IMyInterface { id: string; } 4 | export enum MyEnum { 5 | A, B, C 6 | }; 7 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-export-type-only.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * any sequence of octets 3 | */ 4 | export type Binary = any; 5 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-export-type-reexport-enum.ts: -------------------------------------------------------------------------------- 1 | export enum FemRendererModel { 2 | AppModel = "AppModel", 3 | RouteModel = "RouteModel", 4 | } 5 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-export-type-reexport.ts: -------------------------------------------------------------------------------- 1 | export type RouterContext = { 2 | mock: boolean; 3 | service: string; 4 | path: string; 5 | }; 6 | 7 | export * from "./ts-export-type-reexport-enum"; 8 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-export-type.ts: -------------------------------------------------------------------------------- 1 | export class Class { } 2 | export type StringOrBoolean = string | boolean; -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-index.ts: -------------------------------------------------------------------------------- 1 | export * from "./_private_"; 2 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-re-export-type-only.ts: -------------------------------------------------------------------------------- 1 | export * from "./ts-export-type-only"; 2 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/typescript/ts-re-export.ts: -------------------------------------------------------------------------------- 1 | export * from "module"; 2 | export * from "./having-a-dash"; 3 | export * from "path/having-a-dash"; 4 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/wrapping/export-const.js: -------------------------------------------------------------------------------- 1 | const X = {}; 2 | 3 | export default X; 4 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/wrapping/export-jsdoc-global.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This @exports prop causes sap.ui.define exports flag to be true. 3 | * 4 | * @global 5 | */ 6 | export default {}; 7 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/wrapping/export-literal.js: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /packages/plugin/__test__/fixtures/wrapping/export-options-global.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This test plugin opt to export global 3 | */ 4 | export default {}; 5 | -------------------------------------------------------------------------------- /packages/plugin/__test__/options.js: -------------------------------------------------------------------------------- 1 | import { parse } from "path"; 2 | 3 | /** 4 | * Provides the ability to set options per test file or directory. 5 | */ 6 | const Options = { 7 | default: { 8 | namespacePrefix: undefined, 9 | allowUnsafeMixedExports: false, 10 | noExportCollapse: false, 11 | noExportExtend: false, 12 | noImportInteropPrefixes: ["sap/"], 13 | }, 14 | files: { 15 | "class-convert-never.controller": { 16 | neverConvertClass: true, 17 | }, 18 | "class-controller-w-oninit": { 19 | moveControllerPropsToOnInit: true, 20 | }, 21 | "class-controller-wo-oninit": { 22 | moveControllerPropsToOnInit: true, 23 | }, 24 | "export-options-global": { 25 | exportAllGlobal: true, 26 | }, 27 | "class-convert-controller-extend-static-assign": { 28 | addControllerStaticPropsToExtend: true, 29 | }, 30 | "class-convert-controller-extend-static-prop": { 31 | addControllerStaticPropsToExtend: true, 32 | }, 33 | "class-convert-controller-extension-static-prop-compatibility.controller": { 34 | overridesToOverride: true, 35 | }, 36 | "class-convert-all": { 37 | autoConvertAllExtendClasses: true, 38 | }, 39 | "class-convert-constructor-move.controller": { 40 | moveControllerConstructorToOnInit: true, 41 | }, 42 | "class-convert-constructor-keep-annot.controller": { 43 | moveControllerConstructorToOnInit: true, 44 | }, 45 | "import-modules-map": { 46 | modulesMap: { 47 | ["@babel/polyfill"]: "./vendor/browser-polyfill", 48 | }, 49 | }, 50 | "import-modules-map-fn": { 51 | modulesMap: (src) => Options.files["import-modules-map"].modulesMap[src], 52 | }, 53 | "ts-class-props-only-move-this": { 54 | onlyMoveClassPropsUsingThis: true, 55 | }, 56 | "class-convert-only-move-this-binding": { 57 | onlyMoveClassPropsUsingThis: true, 58 | }, 59 | }, 60 | dirs: { 61 | comments: { 62 | noWrapBeforeImport: true, 63 | }, 64 | "min-wrap": { 65 | noWrapBeforeImport: true, 66 | }, 67 | "never-use-strict": { 68 | noWrapBeforeImport: true, 69 | neverUseStrict: true, 70 | }, 71 | _private_: { 72 | noWrapBeforeImport: true, 73 | moveControllerPropsToOnInit: true, 74 | addControllerStaticPropsToExtend: true, 75 | }, 76 | libs: { 77 | libs: ["^sap/"], 78 | }, 79 | }, 80 | }; 81 | 82 | export function get(filePath) { 83 | const { name, dir: dirPath } = parse(filePath); 84 | const { base: dir } = parse(dirPath); 85 | let options = { ...Options.default }; 86 | if (filePath.includes("prefix")) { 87 | options.namespacePrefix = "prefix"; 88 | } 89 | const fileOverrides = Options.files[name]; 90 | if (fileOverrides) { 91 | options = { 92 | ...options, 93 | ...fileOverrides, 94 | }; 95 | } 96 | const dirOverride = Options.dirs[dir]; 97 | if (dirOverride) { 98 | options = { 99 | ...options, 100 | ...dirOverride, 101 | }; 102 | } 103 | return options; 104 | } 105 | -------------------------------------------------------------------------------- /packages/plugin/__test__/test.js: -------------------------------------------------------------------------------- 1 | /* global test, expect, describe */ 2 | import { 3 | writeFileSync, 4 | statSync, 5 | readdirSync, 6 | emptyDirSync, 7 | ensureDirSync, 8 | } from "fs-extra"; 9 | import { join, resolve } from "path"; 10 | import { transformFileSync } from "@babel/core"; 11 | import { get as getOpts } from "./options"; 12 | import plugin from "../src"; 13 | 14 | const FIXTURE_DIR_NAME = "fixtures"; 15 | const OUT_DIR_NAME = "__output__"; 16 | 17 | emptyDirSync(join(__dirname, OUT_DIR_NAME)); 18 | 19 | function processDirectory(dir) { 20 | const items = readdirSync(dir); 21 | // Process Files first 22 | items 23 | .filter((item) => item.endsWith(".js") || item.endsWith(".ts")) 24 | .forEach((filename) => { 25 | test(filename, () => { 26 | const filePath = join(dir, filename); 27 | let outputPath = filePath 28 | .replace(FIXTURE_DIR_NAME, OUT_DIR_NAME) 29 | .replace(/.ts$/, ".js"); 30 | 31 | try { 32 | const opts = getOpts(filePath); 33 | const presets = []; 34 | const plugins = [ 35 | [plugin, opts], // we don't rely on the plugin order anymore! 36 | "@babel/plugin-syntax-dynamic-import", 37 | "@babel/plugin-syntax-object-rest-spread", 38 | ["@babel/plugin-syntax-decorators", { legacy: true }], 39 | ["@babel/plugin-syntax-class-properties", { useBuiltIns: true }], 40 | ]; 41 | 42 | if (filePath.endsWith(".ts")) { 43 | presets.push(["@babel/preset-typescript"]); 44 | } 45 | 46 | if (filePath.includes("flow")) { 47 | presets.push(["@babel/preset-flow"]); 48 | } 49 | 50 | if (filePath.includes("preset-env")) { 51 | presets.push([ 52 | "@babel/preset-env", 53 | { 54 | targets: undefined, // default targets for preset-env is ES5 55 | modules: false, 56 | useBuiltIns: "usage", // will include imports to corejs (some tests rely on this) 57 | corejs: 2, 58 | }, 59 | ]); 60 | } 61 | 62 | if (filePath.endsWith("property-mutators.js")) { 63 | plugins.push("@babel/plugin-transform-property-mutators"); 64 | } 65 | 66 | if (filePath.includes("/decorators/")) { 67 | plugins.push([ 68 | "@babel/plugin-proposal-decorators", 69 | { legacy: true }, 70 | ]); 71 | } 72 | 73 | const result = transformFileSync(filePath, { 74 | plugins, 75 | presets, 76 | sourceRoot: __dirname, 77 | comments: 78 | filePath.includes("comments") || filename.includes("copyright") || filename.includes("controller-extension-usage"), 79 | babelrc: false, 80 | }).code; 81 | 82 | ensureDirSync(dir.replace(FIXTURE_DIR_NAME, OUT_DIR_NAME)); // This is delayed for when we run with a filter. 83 | writeFileSync(outputPath, result); // For manual verification 84 | 85 | if (filePath.includes("-error-")) { 86 | throw new Error(`Expected ${filename} to throw error`); 87 | } 88 | // if (!opts.allowMixedExports && result.includes(`"__esModule"`)) { 89 | // throw new Error(`Unexpected __esModule declaration in ${filename}`) 90 | // } 91 | if (!filePath.includes("_private_")) { 92 | expect(result).toMatchSnapshot(); 93 | } 94 | } catch (error) { 95 | if (filename.includes("error-")) { 96 | const message = error.message 97 | .replace(filePath, "") 98 | .replace(": ", ""); 99 | outputPath = outputPath.replace(".js", ".txt"); 100 | expect(message).toMatchSnapshot(); 101 | ensureDirSync(dir.replace(FIXTURE_DIR_NAME, OUT_DIR_NAME)); // This is delayed for when we run with a filter. 102 | writeFileSync(outputPath, message); // For manual verification 103 | } else { 104 | throw error; 105 | } 106 | } 107 | }); 108 | }); 109 | 110 | // Recurse into directories 111 | items 112 | .map((name) => ({ name, path: join(dir, name) })) 113 | .filter((item) => statSync(item.path).isDirectory()) 114 | .forEach((item) => { 115 | describe(item.name, () => { 116 | processDirectory(item.path); 117 | }); 118 | }); 119 | } 120 | 121 | (() => { 122 | processDirectory(resolve(__dirname, FIXTURE_DIR_NAME)); 123 | })(); 124 | -------------------------------------------------------------------------------- /packages/plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-transform-modules-ui5", 3 | "version": "7.7.1", 4 | "description": "An unofficial babel plugin for SAP UI5.", 5 | "main": "dist/index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/ui5-community/babel-plugin-transform-modules-ui5.git", 9 | "directory": "packages/plugin" 10 | }, 11 | "engines": { 12 | "node": ">=6" 13 | }, 14 | "scripts": { 15 | "clean": "rimraf dist", 16 | "build": "babel src -d dist", 17 | "build:watch": "babel src -d dist --watch", 18 | "lint": "eslint src", 19 | "lint:fix": "npm run lint -- --fix", 20 | "lint:staged": "lint-staged", 21 | "test": "FORCE_COLOR=0 jest __test__ --coverage=no", 22 | "test:watch": "npm test -- --watch", 23 | "test:update-snapshot": "FORCE_COLOR=0 jest --updateSnapshot", 24 | "prepare": "npm test && npm run build", 25 | "format": "prettier --write 'src/**/*.js'" 26 | }, 27 | "keywords": [ 28 | "ui5", 29 | "sap", 30 | "sapui5", 31 | "openui5", 32 | "babel", 33 | "babel7", 34 | "babeljs", 35 | "babel-plugin", 36 | "babelplugin", 37 | "es6", 38 | "module", 39 | "import", 40 | "export", 41 | "typescript", 42 | "ts" 43 | ], 44 | "author": "Ryan Murphy", 45 | "license": "MIT", 46 | "bugs": { 47 | "url": "https://github.com/r-murphy/babel-plugin-transform-modules-ui5/issues" 48 | }, 49 | "homepage": "https://github.com/r-murphy/babel-plugin-transform-modules-ui5#readme", 50 | "jest": { 51 | "collectCoverage": true 52 | }, 53 | "lint-staged": { 54 | "src/**/*.js": [ 55 | "eslint --fix", 56 | "git add" 57 | ] 58 | }, 59 | "dependencies": { 60 | "array-flatten": "^3.0.0", 61 | "doctrine": "^3.0.0", 62 | "ignore-case": "^0.1.0", 63 | "object-assign-defined": "^1.0.2" 64 | }, 65 | "peerDependencies": { 66 | "@babel/core": "*" 67 | }, 68 | "devDependencies": { 69 | "@babel/cli": "^7.25.6", 70 | "@babel/core": "^7.25.2", 71 | "@babel/eslint-parser": "^7.25.1", 72 | "@babel/plugin-proposal-decorators": "^7.24.7", 73 | "@babel/plugin-proposal-object-rest-spread": "^7.20.7", 74 | "@babel/plugin-syntax-class-properties": "^7.12.13", 75 | "@babel/plugin-syntax-decorators": "^7.24.7", 76 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 77 | "@babel/plugin-syntax-object-rest-spread": "^7.8.3", 78 | "@babel/plugin-transform-property-mutators": "^7.24.7", 79 | "@babel/plugin-transform-typescript": "^7.25.2", 80 | "@babel/preset-env": "^7.25.4", 81 | "@babel/preset-flow": "^7.24.7", 82 | "@babel/preset-typescript": "^7.24.7", 83 | "babel-eslint": "^10.1.0", 84 | "babel-jest": "^29.7.0", 85 | "core-js": "^3.38.1", 86 | "eslint": "^8.54.0", 87 | "eslint-config-standard": "^17.1.0", 88 | "eslint-plugin-import": "^2.30.0", 89 | "eslint-plugin-node": "^11.1.0", 90 | "eslint-plugin-prettier": "^5.2.1", 91 | "eslint-plugin-promise": "^7.1.0", 92 | "eslint-plugin-standard": "^5.0.0", 93 | "husky": "^9.1.6", 94 | "jest": "^29.7.0", 95 | "lint-staged": "^15.2.10", 96 | "prettier": "^3.3.3", 97 | "rimraf": "^6.0.1" 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /packages/plugin/src/classes/helpers/classes.js: -------------------------------------------------------------------------------- 1 | import { types as t } from "@babel/core"; 2 | 3 | import Path from "path"; 4 | import assignDefined from "object-assign-defined"; 5 | 6 | import * as th from "../../utils/templates"; 7 | import * as ast from "../../utils/ast"; 8 | 9 | import { getJsDocClassInfo, getTags } from "./jsdoc"; 10 | import { getDecoratorClassInfo } from "./decorators"; 11 | import { getImportDeclaration } from "./imports"; 12 | 13 | /** 14 | * Converts an ES6 class to a UI5 extend. 15 | * Any static methods or properties will be moved outside the class body. 16 | * The path will be updated with the new AST. 17 | */ 18 | export function convertClassToUI5Extend( 19 | path, 20 | node, 21 | classInfo, 22 | extraStaticProps, 23 | importDeclarationPaths, 24 | opts 25 | ) { 26 | if (!(t.isClassDeclaration(node) || t.isClassExpression(node))) { 27 | return node; 28 | } 29 | 30 | const CONTROLLER_EXTENSION_TAG = "transformControllerExtension"; 31 | const staticMembers = []; 32 | 33 | const classNameIdentifier = node.id; 34 | const className = classNameIdentifier.name; 35 | const superClass = node.superClass; // Identifier node. 36 | const superClassName = superClass.name; 37 | 38 | const isController = 39 | className.includes("Controller") || !!classInfo.controller; 40 | 41 | const moveControllerConstructorToOnInit = 42 | isController && !!opts.moveControllerConstructorToOnInit; 43 | const moveControllerPropsToOnInit = 44 | isController && 45 | (!!opts.moveControllerPropsToOnInit || 46 | !!opts.moveControllerConstructorToOnInit); 47 | const moveStaticStaticPropsToExtend = 48 | isController && !!opts.addControllerStaticPropsToExtend; 49 | const alwaysMoveInstanceProps = !opts.onlyMoveClassPropsUsingThis; 50 | 51 | const extendProps = []; 52 | const boundProps = []; 53 | 54 | const CONSTRUCTOR = "constructor"; 55 | const propsByName = {}; 56 | let constructor; 57 | let constructorComments; 58 | 59 | const staticPropsToAdd = moveStaticStaticPropsToExtend 60 | ? Object.keys(extraStaticProps) 61 | : ["metadata", "renderer", "overrides"]; 62 | 63 | for (const propName of staticPropsToAdd) { 64 | if (extraStaticProps[propName]) { 65 | extendProps.push( 66 | t.objectProperty(t.identifier(propName), extraStaticProps[propName]) 67 | ); 68 | } 69 | } 70 | 71 | for (const memberPath of path.get("body.body")) { 72 | const member = memberPath.node; 73 | const memberName = member.key.name; 74 | 75 | if (t.isClassMethod(member)) { 76 | const isConstructor = member.kind === "constructor"; 77 | const membersToAssign = []; 78 | const params = isConstructor 79 | ? member.params?.map((param) => { 80 | // handling of parameter properties for constructors (TypeScript): 81 | // https://www.typescriptlang.org/docs/handbook/2/classes.html#parameter-properties 82 | // -> extracting the real parameters and store the members to assign 83 | if (param.type === "TSParameterProperty") { 84 | membersToAssign.push(param.parameter); 85 | return param.parameter; 86 | } 87 | return param; 88 | }) 89 | : member.params; 90 | const func = t.functionExpression( 91 | member.key, 92 | params, 93 | member.body, 94 | member.generator, 95 | member.async 96 | ); 97 | if (isConstructor && membersToAssign.length > 0) { 98 | // handling of parameter properties for constructors (TypeScript): 99 | // -> assigning parameter properties as members to the instance 100 | const newMembers = membersToAssign.map((member) => 101 | buildMemberAssignmentStatement(t.thisExpression(), { 102 | key: member, 103 | computed: false, 104 | value: member, 105 | }) 106 | ); 107 | const superIndex = member.body.body.findIndex( 108 | (node) => 109 | ast.isSuperCallExpression(node.expression) || 110 | ast.isSuperPrototypeCallOf( 111 | node.expression, 112 | superClassName, 113 | "constructor" 114 | ) 115 | ); 116 | member.body.body.splice( 117 | superIndex === -1 ? member.body.body.length : superIndex + 1, 118 | 0, 119 | ...newMembers 120 | ); 121 | } 122 | if (member.static) { 123 | staticMembers.push( 124 | buildMemberAssignmentStatement(classNameIdentifier, { 125 | ...member, 126 | value: func, 127 | }) 128 | ); 129 | } else { 130 | propsByName[memberName] = func; 131 | if (member.kind === "get" || member.kind === "set") { 132 | extendProps.push( 133 | t.objectMethod( 134 | member.kind, 135 | member.key, 136 | member.params, 137 | member.body, 138 | member.computed 139 | ) 140 | ); 141 | } else { 142 | // method 143 | if (memberName === CONSTRUCTOR) { 144 | constructorComments = member.leadingComments; 145 | constructor = func; 146 | if (moveControllerPropsToOnInit) { 147 | continue; // don't push to props yet 148 | } 149 | } 150 | func.id = path.scope.generateUidIdentifier(func.id.name); // Give the function a unique name 151 | extendProps.push( 152 | buildObjectProperty({ 153 | ...member, 154 | value: func, 155 | }) 156 | ); 157 | } 158 | } 159 | } else if (t.isClassProperty(member)) { 160 | // For class properties annotated to represent controller extensions, replace the pure declaration with an assignment (that's what the runtime expects) 161 | // and keep them at the initialization object as properties (don't move into constructor). 162 | if ( 163 | member.leadingComments?.some((comment) => { 164 | return comment.value.includes("@" + CONTROLLER_EXTENSION_TAG); 165 | }) || 166 | member.decorators?.some((decorator) => { 167 | return decorator.expression?.name === CONTROLLER_EXTENSION_TAG; 168 | }) 169 | ) { 170 | const typeAnnotation = member.typeAnnotation?.typeAnnotation; 171 | // double-check that it is a valid node for a controller extension 172 | if ( 173 | t.isTSTypeReference(typeAnnotation) || 174 | t.isTSQualifiedName(typeAnnotation) 175 | ) { 176 | const typeName = getTypeName(typeAnnotation); 177 | 178 | // 1. transform the property from being typed as instance and un-initialized to a property where the controller extension *class* is assigned as value 179 | const valueIdentifier = t.identifier(typeName); 180 | member.value = valueIdentifier; 181 | member.typeAnnotation = null; 182 | extendProps.unshift(buildObjectProperty(member)); // add it to the properties of the extend() config object 183 | 184 | // 2. add a binding reference to the value, so in case the TS transpiler runs later it recognizes that the import is still needed 185 | const typeNameFirstPart = typeName.split(".")[0]; // e.g. when "myExtension: someBundle.MyExtension" 186 | if (memberPath.scope.hasBinding(typeNameFirstPart)) { 187 | const binding = path.scope.getBinding(typeNameFirstPart); 188 | binding.referencePaths.push(memberPath.get("value")); 189 | } 190 | 191 | // 3. restore the import in case it was run already and removed the import 192 | const neededImportDeclaration = getImportDeclaration( 193 | memberPath?.hub?.file?.opts?.filename, 194 | typeName 195 | ); 196 | if ( 197 | !importDeclarationPaths.some( 198 | (path) => path.node === neededImportDeclaration 199 | ) 200 | ) { 201 | // TODO: import might be there but with the specifier removed; we can clone, but then other specifiers are duplicate 202 | // if import is no longer there, re-add it 203 | importDeclarationPaths[ 204 | importDeclarationPaths.length - 1 205 | ].insertAfter(neededImportDeclaration); 206 | } 207 | 208 | // 4. prevent the member from also being added to the constructor (member does have a value now and initializer would be added below) 209 | continue; 210 | } 211 | } 212 | 213 | if (!member.value) continue; // remove all other un-initialized static class props (typescript) 214 | 215 | // Now handle the case where an extended controller extension - not just e.g. "Routing", but 216 | // "Routing.override({...})" - is assigned to the class property. To make this work in TypeScript code 217 | // (Routing.override({...}) returns a class, but the "routing" member property needs to be typed as an instance), 218 | // we require the following notation, using a dummy function ControllerExtension.use() that does actually 219 | // not exist at runtime, but does the required class-to-instance conversion from TypeScript perspective: 220 | // routing: ControllerExtension.use(Routing.override({...})) 221 | // In this case, the code in the "if" clause above was not executed, regardless of the presence of the 222 | // @transformControllerExtension marker, because it is not a type assignment. In the resulting code, the 223 | // "ControllerExtension.use(...)" part should be removed and the content of the brackets should be assigned 224 | // directly to the member property. 225 | const rightSide = member.value; 226 | if (isCallToControllerExtensionUse(rightSide, memberPath)) { 227 | member.value = rightSide.arguments[0]; 228 | extendProps.unshift(buildObjectProperty(member)); // add it to the properties of the extend() config object 229 | continue; // prevent the member from also being added to the constructor 230 | } 231 | 232 | // code instrumentation sometimes wraps ControllerExtension.use() calls like: 233 | // this.routing = (cov_1uvvg22e7l().s[5]++, ControllerExtension.use(Routing.override({ … }))); 234 | if ( 235 | t.isSequenceExpression(rightSide) && 236 | isCallToControllerExtensionUse( 237 | rightSide.expressions[rightSide.expressions.length - 1], 238 | memberPath 239 | ) 240 | ) { 241 | rightSide.expressions[rightSide.expressions.length - 1] = 242 | rightSide.expressions[rightSide.expressions.length - 1].arguments[0]; 243 | member.value = rightSide; 244 | extendProps.unshift(buildObjectProperty(member)); // add it to the properties of the extend() config object 245 | continue; // prevent the member from also being added to the constructor 246 | } 247 | 248 | // Special handling for TypeScript limitation where metadata, renderer and overrides must be properties. 249 | if (["metadata", "renderer", "overrides"].includes(memberName)) { 250 | if (opts.overridesToOverride && member.key.name === "overrides") { 251 | member.key.name = "override"; 252 | } 253 | extendProps.unshift(buildObjectProperty(member)); 254 | } else if (member.static) { 255 | if (moveStaticStaticPropsToExtend) { 256 | extendProps.unshift(buildObjectProperty(member)); 257 | } else { 258 | staticMembers.push( 259 | buildMemberAssignmentStatement(classNameIdentifier, member) 260 | ); 261 | } 262 | } else { 263 | propsByName[memberName] = member.value; 264 | if (memberName === "constructor") { 265 | constructorComments = member.leadingComments; 266 | constructor = member.value; 267 | if (moveControllerPropsToOnInit) { 268 | continue; // don't push to props yet 269 | } 270 | } 271 | if ( 272 | alwaysMoveInstanceProps || 273 | t.isArrowFunctionExpression(member.value) || 274 | ast.isThisExpressionUsed(member.value) 275 | ) { 276 | boundProps.push(member); 277 | } else { 278 | extendProps.push(buildObjectProperty(member)); 279 | } 280 | } 281 | } 282 | } 283 | 284 | /** 285 | * Checks whether the given thing is a CallExpression that calls ControllerExtension.use(...) 286 | * 287 | * @param {*} expression the thing to check - does not need to be a CallExpression 288 | * @param {string} memberPath 289 | * @returns true if the given expression is a CallExpression that calls ControllerExtension.use(...) 290 | */ 291 | function isCallToControllerExtensionUse(expression, memberPath) { 292 | if (!t.isCallExpression(expression)) { 293 | return false; 294 | } 295 | const callee = expression.callee; 296 | if ( 297 | t.isMemberExpression(callee) && 298 | t.isIdentifier(callee.object) && 299 | t.isIdentifier(callee.property) && 300 | callee.property.name === "use" // we are looking for "ControllerExtension.use(...)" 301 | ) { 302 | const importDeclaration = getImportDeclaration( 303 | memberPath?.hub?.file?.opts?.filename, 304 | callee?.object?.name // usually, but not necessarily always: "ControllerExtension"... 305 | ); 306 | // ...hence we rather look at the imported module name to be sure 307 | if ( 308 | importDeclaration?.source?.value === 309 | "sap/ui/core/mvc/ControllerExtension" 310 | ) { 311 | if (!expression.arguments || expression.arguments.length !== 1) { 312 | // exactly one argument must be there 313 | throw memberPath.buildCodeFrameError( 314 | `ControllerExtension.use() must be called with exactly one argument but has ${ 315 | expression.arguments ? expression.arguments.length : 0 316 | }` 317 | ); 318 | } 319 | return true; 320 | } 321 | } 322 | return false; // return false if not a match 323 | } 324 | 325 | // Arrow function properties need to get moved to the constructor so that 326 | // they're bound properly to the class instance, to align with the spec. 327 | // For controllers, use onInit rather than constructor, since controller constructors don't work. 328 | // Also move the constructor's statements to the onInit. 329 | 330 | const bindToConstructor = !moveControllerPropsToOnInit; 331 | const bindToMethodName = moveControllerPropsToOnInit 332 | ? "onInit" 333 | : "constructor"; 334 | 335 | // avoid getting a prop named constructor as it may return {}'s 336 | let bindMethod = moveControllerPropsToOnInit 337 | ? propsByName[bindToMethodName] 338 | : constructor; 339 | 340 | const constructorJsdoc = getTags(constructorComments); 341 | 342 | const keepConstructor = 343 | !moveControllerConstructorToOnInit || 344 | classInfo.keepConstructor || 345 | constructorJsdoc.keep; 346 | 347 | // See if we need either constructor or onInit 348 | const needsBindingMethod = 349 | boundProps.length || 350 | (moveControllerPropsToOnInit && constructor && !keepConstructor); 351 | 352 | // See if we need to create a new 'constructor' or 'onInit' method, depending which one we'll bind to. 353 | if (needsBindingMethod && !bindMethod) { 354 | const bindToId = t.identifier(bindToMethodName); 355 | const bindMethodDeclaration = bindToConstructor 356 | ? th.buildInheritingConstructor({ 357 | SUPER: t.identifier(superClassName), 358 | }) 359 | : th.buildInheritingFunction({ 360 | NAME: bindToId, 361 | SUPER: t.identifier(superClassName), 362 | }); 363 | bindMethod = ast.convertFunctionDeclarationToExpression( 364 | bindMethodDeclaration 365 | ); 366 | extendProps.unshift(t.objectProperty(bindToId, bindMethod)); 367 | } 368 | 369 | if (constructor && moveControllerPropsToOnInit) { 370 | if (keepConstructor) { 371 | extendProps.unshift( 372 | t.objectProperty(t.identifier(CONSTRUCTOR), constructor) 373 | ); 374 | } else { 375 | // Copy all except the super call from the constructor to the bindMethod (i.e. onInit) 376 | bindMethod.body.body.unshift( 377 | ...constructor.body.body.filter( 378 | (node) => 379 | !( 380 | ast.isSuperCallExpression(node.expression) || 381 | ast.isSuperPrototypeCallOf( 382 | node.expression, 383 | superClassName, 384 | "constructor" 385 | ) 386 | ) 387 | ) 388 | ); 389 | } 390 | } 391 | 392 | if (boundProps.length) { 393 | // We need to inject the bound props into the bind method (constructor or onInit), 394 | // but not until after the super call (if applicable) 395 | 396 | const mappedProps = boundProps.map((member) => 397 | buildThisMemberAssignmentStatement(member) 398 | ); 399 | 400 | const superIndex = bindMethod.body.body.findIndex( 401 | (node) => 402 | ast.isSuperCallExpression(node.expression) || 403 | ast.isSuperPrototypeCallOf( 404 | node.expression, 405 | superClassName, 406 | bindToMethodName 407 | ) 408 | ); 409 | if (superIndex === -1) { 410 | // If there's no super, just add the bound props at the start 411 | bindMethod.body.body.unshift(...mappedProps); 412 | } else { 413 | const upToSuper = bindMethod.body.body.slice(0, superIndex + 1); 414 | const afterSuper = bindMethod.body.body.slice(superIndex + 1); 415 | bindMethod.body.body = [...upToSuper, ...mappedProps, ...afterSuper]; 416 | } 417 | } 418 | 419 | const extendAssign = th.buildExtendAssign({ 420 | NAME: classNameIdentifier, 421 | SUPER: superClass, // Needs Identifier node 422 | FQN: t.stringLiteral(getFullyQualifiedName(classInfo)), 423 | OBJECT: t.objectExpression(extendProps), 424 | }); 425 | 426 | return [extendAssign, ...staticMembers]; 427 | } 428 | 429 | function getFullyQualifiedName(classInfo) { 430 | if (classInfo.alias) return classInfo.alias; 431 | if (classInfo.name) return classInfo.name; 432 | const namespace = classInfo.namespace || classInfo.fileNamespace; 433 | const separator = namespace ? "." : ""; 434 | return `${namespace}${separator}${classInfo.localName}`; 435 | } 436 | 437 | export function getClassInfo(path, node, parent, pluginOpts) { 438 | const defaults = { 439 | localName: node.id.name, 440 | superClassName: node.superClass && node.superClass.name, 441 | fileNamespace: getFileBaseNamespace(path, pluginOpts) || "", 442 | }; 443 | const decoratorInfo = getDecoratorClassInfo(node); 444 | const jsDocInfo = getJsDocClassInfo(node, parent); 445 | 446 | // like Object.assign, but ignoring undefined values. 447 | return assignDefined(defaults, decoratorInfo, jsDocInfo); 448 | } 449 | 450 | /** 451 | * Reads the namespace from the file path (but not the name). 452 | */ 453 | function getFileBaseNamespace(path, pluginOpts) { 454 | const opts = path.hub.file.opts; 455 | const filename = Path.resolve(opts.filename); 456 | const sourceRoot = opts.sourceRoot 457 | ? Path.resolve(process.cwd(), opts.sourceRoot) 458 | : process.cwd(); 459 | if (filename.startsWith(sourceRoot)) { 460 | const filenameRelative = Path.relative(sourceRoot, filename); 461 | const { dir } = Path.parse(filenameRelative); 462 | const namespaceParts = dir.split(Path.sep); 463 | if (pluginOpts.namespacePrefix) { 464 | namespaceParts.unshift(pluginOpts.namespacePrefix); 465 | } 466 | return namespaceParts.join("."); 467 | } else { 468 | return undefined; 469 | } 470 | } 471 | 472 | const getQualifiedName = (node) => { 473 | let { left, right } = node; 474 | 475 | // if left is TSQualifiedName, recursive call to get full namespace 476 | if (t.isTSQualifiedName(left)) { 477 | left = getQualifiedName(left); 478 | } else { 479 | // if left is an Identifier 480 | left = left.name; 481 | } 482 | 483 | return `${left}.${right.name}`; 484 | }; 485 | 486 | export const getTypeName = (typeAnnotation) => { 487 | if (t.isTSTypeReference(typeAnnotation)) { 488 | // for TSTypeReference, typeName can be an Identifier or a TSQualifiedName 489 | return ( 490 | typeAnnotation.typeName.name || getQualifiedName(typeAnnotation.typeName) 491 | ); 492 | } 493 | if (t.isTSQualifiedName(typeAnnotation)) { 494 | // for TSQualifiedName 495 | return getQualifiedName(typeAnnotation); 496 | } 497 | return null; 498 | }; 499 | 500 | const buildObjectProperty = (member) => { 501 | const newObjectProperty = t.objectProperty( 502 | member.key, 503 | member.value, 504 | member.computed 505 | ); 506 | newObjectProperty.leadingComments = member.leadingComments; 507 | return newObjectProperty; 508 | }; 509 | 510 | const buildMemberAssignmentStatement = (objectIdentifier, member) => { 511 | const newMember = t.expressionStatement( 512 | t.assignmentExpression( 513 | "=", 514 | t.memberExpression(objectIdentifier, member.key, member.computed), 515 | member.value 516 | ) 517 | ); 518 | newMember.leadingComments = member.leadingComments; 519 | return newMember; 520 | }; 521 | 522 | const buildThisMemberAssignmentStatement = buildMemberAssignmentStatement.bind( 523 | null, 524 | t.thisExpression() 525 | ); 526 | -------------------------------------------------------------------------------- /packages/plugin/src/classes/helpers/decorators.js: -------------------------------------------------------------------------------- 1 | const classInfoValueTags = [ 2 | "alias", 3 | "name", 4 | "namespace", 5 | "metadata", 6 | "renderer", 7 | ]; 8 | 9 | const classInfoBoolTags = ["nonUI5"]; 10 | 11 | export function getDecoratorClassInfo(node) { 12 | const decorators = node.decorators; 13 | if (!decorators || !decorators.length) { 14 | return {}; 15 | } 16 | const decoratorsByName = groupByName(decorators); 17 | const info = {}; 18 | for (const tagName of classInfoValueTags) { 19 | const value = getDecoratorValue(decoratorsByName[tagName.toLowerCase()]); 20 | if (value) { 21 | info[tagName] = value; 22 | } 23 | } 24 | for (const tagName of classInfoBoolTags) { 25 | const value = !!decoratorsByName[tagName.toLowerCase()]; 26 | if (value) { 27 | info[tagName] = value; 28 | } 29 | } 30 | return info; 31 | } 32 | 33 | function groupByName(decorators) { 34 | return decorators.reduce((accumulator, decorator) => { 35 | const expression = decorator.expression; 36 | const name = 37 | expression.name || (expression.callee && expression.callee.name); 38 | accumulator[name.toLowerCase()] = decorator; 39 | return accumulator; 40 | }, {}); 41 | } 42 | 43 | function getDecoratorValue(decorator) { 44 | return ((decorator && decorator.expression.arguments[0]) || {}).value; 45 | } 46 | -------------------------------------------------------------------------------- /packages/plugin/src/classes/helpers/imports.js: -------------------------------------------------------------------------------- 1 | import { types as t } from "@babel/core"; 2 | 3 | let importDeclarations = {}; 4 | 5 | export const saveImports = (file) => { 6 | // save all import declarations before "unneeded" ones are removed by the TypeScript plugin 7 | importDeclarations[file.opts.filename] = file.ast.program.body.filter( 8 | t.isImportDeclaration 9 | ); // right now even the removed import still exists later and can be re-added. Otherwise do: .map(decl => t.cloneNode(decl)); 10 | }; 11 | 12 | // can be called from visitor to access previously present declarations 13 | export function getImportDeclaration(filename, typeName) { 14 | if (!filename || !typeName) { 15 | return null; 16 | } 17 | 18 | const typeNameParts = typeName.split("."); 19 | 20 | // find the declaration importing the typeName among the collected import declarations in this file 21 | const filteredDeclarations = importDeclarations[filename].filter( 22 | (importDeclaration) => { 23 | // each import declaration can import several entities, so let's check all of them 24 | for (let specifier of importDeclaration.specifiers) { 25 | if ( 26 | (t.isImportDefaultSpecifier(specifier) || 27 | t.isImportNamespaceSpecifier(specifier)) && 28 | specifier.local.name === typeNameParts[0] 29 | ) { 30 | // if the import is default, then the typeName should only have one part (the import name) 31 | return true; 32 | } else if ( 33 | t.isImportSpecifier(specifier) && 34 | specifier.imported.name === typeNameParts[typeNameParts.length - 1] 35 | ) { 36 | // If the import is named, then the last part of the typeName should match the imported name 37 | return true; 38 | } 39 | } 40 | } 41 | ); 42 | return filteredDeclarations[0]; // should be exactly one 43 | } 44 | -------------------------------------------------------------------------------- /packages/plugin/src/classes/helpers/jsdoc.js: -------------------------------------------------------------------------------- 1 | import { types as t } from "@babel/core"; 2 | import doctrine from "doctrine"; 3 | import ignoreCase from "ignore-case"; 4 | 5 | const classInfoValueTags = ["alias", "name", "namespace"]; 6 | const classInfoBoolTags = ["nonUI5", "controller", "keepConstructor"]; 7 | 8 | export function getJsDocClassInfo(node, parent) { 9 | if (node.leadingComments) { 10 | return node.leadingComments 11 | .filter(isCommentBlock) 12 | .map((comment) => { 13 | const docAST = doctrine.parse(comment.value, { 14 | unwrap: true, 15 | }); 16 | const tags = docAST.tags || []; 17 | const info = {}; 18 | for (const tagName of classInfoValueTags) { 19 | const value = getJsDocTagValue(tags, tagName); 20 | if (value) { 21 | info[tagName] = value; 22 | } 23 | } 24 | for (const tagName of classInfoBoolTags) { 25 | const value = !!getJsDocTag(tags, tagName); 26 | if (value) { 27 | info[tagName] = value; 28 | } 29 | } 30 | return info; 31 | }) 32 | .filter(notEmpty)[0]; 33 | } 34 | // Else see if the JSDoc are on the return statement (eg. return class X extends SAPClass) 35 | // or export statement (eg. export default class X extends SAPClass) 36 | else if ( 37 | (t.isClassExpression(node) && t.isReturnStatement(parent)) || 38 | (t.isClassDeclaration(node) && t.isExportDefaultDeclaration(parent)) 39 | ) { 40 | return getJsDocClassInfo(parent); 41 | } else { 42 | return {}; 43 | } 44 | } 45 | 46 | /** 47 | * Returns a map of tags by name. 48 | * Converts empty to bool. Also converts bool value 49 | */ 50 | export function getTags(comments) { 51 | if (!comments) { 52 | return {}; 53 | } 54 | for (const comment of comments) { 55 | if (!isCommentBlock(comment)) { 56 | continue; 57 | } 58 | const docAST = doctrine.parse(comment.value, { 59 | unwrap: true, 60 | }); 61 | const tags = docAST.tags; 62 | if (!tags || !tags.length) { 63 | continue; 64 | } 65 | const map = {}; 66 | for (const tag of tags) { 67 | const title = tag.title; 68 | let value = tag.name || tag.description || true; 69 | if (value === "false") value = false; 70 | map[title] = value; 71 | } 72 | return map; 73 | } 74 | return {}; 75 | } 76 | 77 | function getJsDocTagValue(tags, name) { 78 | const tag = getJsDocTag(tags, name); 79 | return tag && (tag.name || tag.description); 80 | } 81 | 82 | function getJsDocTag(tags, name) { 83 | return tags.find((t) => ignoreCase.equals(name, t.title)); 84 | } 85 | 86 | function notEmpty(obj) { 87 | return Object.values(obj).some((value) => value); 88 | } 89 | 90 | export function hasJsdocGlobalExportFlag(node) { 91 | if (!node.leadingComments) { 92 | return false; 93 | } 94 | return node.leadingComments.filter(isCommentBlock).some((comment) => { 95 | return ( 96 | doctrine.parse(comment.value, { 97 | unwrap: true, 98 | tags: ["global"], 99 | }).tags.length > 0 100 | ); 101 | }); 102 | } 103 | 104 | // This doesn't exist on babel-types 105 | function isCommentBlock(node) { 106 | return node && node.type === "CommentBlock"; 107 | } 108 | -------------------------------------------------------------------------------- /packages/plugin/src/classes/pre.js: -------------------------------------------------------------------------------- 1 | import { saveImports } from "./helpers/imports"; 2 | 3 | export const ClassPre = (file) => { 4 | // save all import declarations before "unneeded" ones are removed by the TypeScript plugin 5 | saveImports(file); 6 | }; 7 | -------------------------------------------------------------------------------- /packages/plugin/src/classes/visitor.js: -------------------------------------------------------------------------------- 1 | import { types as t } from "@babel/core"; 2 | import * as th from "../utils/templates"; 3 | import * as ast from "../utils/ast"; 4 | import * as classes from "./helpers/classes"; 5 | 6 | const CONSTRUCTOR = "constructor"; 7 | 8 | export const ClassTransformVisitor = { 9 | ImportDeclaration(path) { 10 | this.importDeclarationPaths.push(path); 11 | }, 12 | ImportDefaultSpecifier(path) { 13 | this.importNames.push(path.node.local.name); 14 | }, 15 | /** 16 | * ClassDeclaration visitor. 17 | * Use both enter() and exit() to track when the visitor is inside a UI5 class, 18 | * in order to convert the super calls. 19 | * No changes for non-UI5 classes. 20 | */ 21 | Class: { 22 | enter(path, { file, opts = {} }) { 23 | const { node } = path; 24 | const className = node?.id?.name; 25 | 26 | if (!className || opts.neverConvertClass) { 27 | return; 28 | } 29 | if (!doesClassExtendFromImport(node, [...this.importNames])) { 30 | // If it doesn't extend from an import, treat it as plain ES2015 class. 31 | return; 32 | } 33 | 34 | // If the super class is one of the imports, we'll assume it's a UI5 managed class, 35 | // and therefore may need to be transformed to .extend() syntax. 36 | const classInfo = classes.getClassInfo(path, node, path.parent, opts); 37 | 38 | // filter found decorators (to be not handled by other plugins) 39 | node.decorators = node.decorators?.filter((d) => { 40 | const exp = d.expression; 41 | const name = exp.name || exp.callee?.name; 42 | return classInfo[name] === undefined; 43 | }); 44 | 45 | if (shouldConvertClass(file, node, opts, classInfo)) { 46 | // Save super class name for converting super calls 47 | this.superClassName = classInfo.superClassName; 48 | // store the classinfo 49 | if (className) { 50 | this.classInfo = this.classInfo || {}; 51 | this.classInfo[className] = classInfo; 52 | } 53 | } 54 | }, 55 | exit(path, { opts = {} }) { 56 | const { node, parent, parentPath } = path; 57 | const className = node?.id?.name; 58 | 59 | // Only if classinfo has been found we process this file 60 | const classInfo = this.classInfo?.[className]; 61 | if (!classInfo || !node.superClass) { 62 | return; 63 | } 64 | 65 | // Find the Block scoped parent (Program or Function body) and search for assigned properties within that (eg. MyClass.X = "X"). 66 | const blockParent = path.findParent((path) => path.isBlock()).node; 67 | const staticProps = ast.groupPropertiesByName( 68 | ast.getOtherPropertiesOfIdentifier(blockParent, className) 69 | ); 70 | 71 | // TODO: flag metadata and renderer for removal if applicable 72 | const ui5ExtendClass = classes.convertClassToUI5Extend( 73 | path, 74 | node, 75 | classInfo, 76 | staticProps, 77 | this.importDeclarationPaths, 78 | opts 79 | ); 80 | 81 | if (path.isClassDeclaration()) { 82 | if (t.isExportDefaultDeclaration(parent)) { 83 | path.parentPath.replaceWithMultiple([ 84 | ...ui5ExtendClass, 85 | th.buildExportDefault({ 86 | VALUE: node.id, 87 | }), 88 | ]); 89 | } else { 90 | // e.g. class X {} 91 | path.replaceWithMultiple(ui5ExtendClass); 92 | } 93 | } else if (path.isClassExpression()) { 94 | //e.g. return class X {} 95 | if (t.isReturnStatement(parent)) { 96 | // Add the return statement back before calling replace 97 | ui5ExtendClass.push( 98 | th.buildReturn({ 99 | ID: t.identifier(classInfo.localName), 100 | }) 101 | ); 102 | } 103 | parentPath.replaceWithMultiple(ui5ExtendClass); 104 | } 105 | 106 | this.superClassName = null; 107 | }, 108 | }, 109 | /*! 110 | * Visits function calls. 111 | */ 112 | CallExpression(path) { 113 | const { node } = path; 114 | const { callee } = node; 115 | // If the file already has sap.ui.define, get the names of variables it creates to use for the class logic. 116 | if (ast.isCallExpressionCalling(node, "sap.ui.define")) { 117 | this.importNames.push( 118 | ...getRequiredParamsOfSAPUIDefine(path, node).map((req) => req.name) 119 | ); 120 | return; 121 | } else if (this.superClassName) { 122 | if (t.isSuper(callee)) { 123 | replaceConstructorSuperCall(path, node, this.superClassName); 124 | } else if (t.isSuper(callee.object)) { 125 | replaceObjectSuperCall(path, node, this.superClassName); 126 | } else if (isSuperApply(callee)) { 127 | replaceSuperApplyCall(path, node, this.superClassName); 128 | } 129 | } 130 | }, 131 | /** 132 | * Convert object method constructor() to constructor: function constructor(), 133 | * since a UI5 class is not a real class. 134 | */ 135 | ObjectMethod(path) { 136 | const { node } = path; 137 | if (node.key.name === CONSTRUCTOR) { 138 | // The keyword 'constructor' should not be used as a shorthand 139 | // method name in an object. It might(?) work on some objects, 140 | // but it doesn't work with X.extend(...) inheritance. 141 | path.replaceWith( 142 | t.objectProperty( 143 | t.identifier(CONSTRUCTOR), 144 | t.functionExpression( 145 | t.identifier(CONSTRUCTOR), 146 | node.params, 147 | node.body 148 | ) 149 | ) 150 | ); 151 | } 152 | }, 153 | }; 154 | 155 | function isSuperApply(callee) { 156 | return ( 157 | t.isIdentifier(callee.property, { name: "apply" }) && 158 | t.isSuper(callee.object.object) 159 | ); 160 | } 161 | 162 | function getRequiredParamsOfSAPUIDefine(path, node) { 163 | const defineArgs = node.arguments; 164 | const callbackNode = defineArgs.find((argNode) => t.isFunction(argNode)); 165 | return callbackNode?.params || []; // Identifier 166 | } 167 | 168 | /** 169 | * Replace super() call 170 | */ 171 | function replaceConstructorSuperCall(path, node, superClassName) { 172 | replaceSuperNamedCall(path, node, superClassName, CONSTRUCTOR); 173 | } 174 | 175 | /** 176 | * Replace super.method() call 177 | */ 178 | function replaceObjectSuperCall(path, node, superClassName) { 179 | replaceSuperNamedCall(path, node, superClassName, node.callee.property.name); 180 | } 181 | 182 | /** 183 | * Replace super.method.apply() call 184 | */ 185 | function replaceSuperApplyCall(path, node, superClassName) { 186 | const methodName = node.callee.object.property.name; 187 | path.replaceWith( 188 | t.callExpression( 189 | t.identifier(`${superClassName}.prototype.${methodName}.apply`), 190 | node.arguments 191 | ) 192 | ); 193 | } 194 | 195 | function replaceSuperNamedCall(path, node, superClassName, methodName) { 196 | // .call() is better for simple args (or not args) but doesn't work right for spread args 197 | // if it gets further transpiled by babel spread args transform (will be .call.apply(...). 198 | const thisEx = t.thisExpression(); 199 | const hasSpread = node.arguments.some(t.isSpreadElement); 200 | const caller = hasSpread ? "apply" : "call"; 201 | const callArgs = hasSpread 202 | ? [thisEx, t.arrayExpression(node.arguments)] 203 | : [thisEx, ...node.arguments]; 204 | path.replaceWith( 205 | t.callExpression( 206 | t.identifier(`${superClassName}.prototype.${methodName}.${caller}`), 207 | callArgs 208 | ) 209 | ); 210 | } 211 | 212 | function doesClassExtendFromImport(node, imports) { 213 | const superClass = node.superClass; 214 | return superClass && imports.some((imported) => imported === superClass.name); 215 | } 216 | 217 | function shouldConvertClass(file, node, opts, classInfo) { 218 | if (classInfo.nonUI5) { 219 | return false; 220 | } 221 | if (opts.autoConvertAllExtendClasses == true) { 222 | return true; 223 | } 224 | if ( 225 | classInfo.name || 226 | classInfo.alias || 227 | classInfo.controller || 228 | classInfo.namespace 229 | ) { 230 | return true; 231 | } 232 | // Convert controller classes 233 | if ( 234 | /.*[.]controller[.](js|ts)$/.test(file.opts.filename) && 235 | opts.autoConvertControllerClass !== false 236 | ) { 237 | return true; 238 | } 239 | return false; 240 | } 241 | -------------------------------------------------------------------------------- /packages/plugin/src/index.js: -------------------------------------------------------------------------------- 1 | import { ClassTransformVisitor } from "./classes/visitor"; 2 | import { ClassPre } from "./classes/pre"; 3 | import { ModuleTransformVisitor } from "./modules/visitor"; 4 | 5 | import { wrap } from "./modules/helpers/wrapper"; 6 | 7 | /* 8 | References: 9 | Babel Plugin Handbook: https://github.com/thejameskyle/babel-handbook/blob/master/translations/en/plugin-handbook.md 10 | AST Spec: https://github.com/babel/babel/blob/master/packages/babel-parser/ast/spec.md 11 | Babel Types (t.*) https://github.com/babel/babel/tree/master/packages/babel-types 12 | AST Explorer: https://astexplorer.net/ 13 | */ 14 | 15 | module.exports = () => { 16 | const UI5Visitor = { 17 | Program: { 18 | enter(path, { opts }) { 19 | if (this.ran) return; 20 | this.ran = true; 21 | 22 | if (opts.onlyConvertNamedClass == false) { 23 | throw new Error( 24 | "ERROR: onlyConvertNamedClass=false is no longer supported. Use autoConvertAllExtendClassesByDefault=true" 25 | ); 26 | } 27 | // TODO: enable this in a later version 28 | // else if (opts.onlyConvertNamedClass == true) { 29 | // console.warn('WARN: onlyConvertNamedClass=true is now the default behaviour') // eslint-disable-line no-console 30 | // } 31 | 32 | // Opts 33 | this.namespacePrefix = opts.namespacePrefix; 34 | this.noImportInteropPrefixes = opts.noImportInteropPrefixes || ["sap/"]; 35 | this.noImportInteropPrefixesRegexp = new RegExp( 36 | this.noImportInteropPrefixes.map((p) => `(^${p}.*)`).join("|") 37 | ); 38 | 39 | // Properties for Module Transform 40 | this.programNode = path.node; 41 | this.parent = path.parent; 42 | this.defaultExport = null; 43 | this.defaultExportNode = null; 44 | this.exportGlobal = false; 45 | this.ignoredImports = []; 46 | this.imports = []; 47 | this.namedExports = []; 48 | this.injectDynamicImportHelper = false; 49 | 50 | // Properties for Class Transform 51 | this.importNames = []; 52 | this.importDeclarationPaths = []; 53 | 54 | // The classes must be converted right away before any other class transforms get a chance to run. 55 | path.traverse(ClassTransformVisitor, this); 56 | }, 57 | exit(path, { opts }) { 58 | path.traverse( 59 | { ImportDeclaration: ModuleTransformVisitor.ImportDeclaration }, 60 | this 61 | ); 62 | wrap(this, path.node, opts); 63 | }, 64 | }, 65 | // The module transform visitor uses normal traversal so we capture 66 | // imports and helper code that get added by other transforms. 67 | ...ModuleTransformVisitor, 68 | }; 69 | 70 | return { 71 | pre: ClassPre, 72 | visitor: UI5Visitor, 73 | }; 74 | }; 75 | -------------------------------------------------------------------------------- /packages/plugin/src/modules/helpers/exports.js: -------------------------------------------------------------------------------- 1 | import { types as t } from "@babel/core"; 2 | import * as th from "../../utils/templates"; 3 | import * as ast from "../../utils/ast"; 4 | 5 | /** 6 | * Collapse named exports onto the default export so that the default export can be returned directly, 7 | * so that exports can be easily used by code that does not include an import interop. 8 | * This includes the following: 9 | * - Ignore named exports that already exist on the default export and which reference the same identifer. 10 | * - If possible, assign any remaining exports to the default export. 11 | * - This cannot be done if there is a naming conflict. 12 | * 13 | * In order to determine what properties the default export already has, the plugin will scan the 14 | * top level of the program to find any assignments, including Object.assign() and _extend() calls. 15 | * It does this recursively in the case of those method calls. 16 | * 17 | * If there are any named exports left, they get returned, so the main plugin can check the opts 18 | * and decide if an error should be thrown. 19 | */ 20 | export function collapseNamedExports( 21 | programNode, 22 | defaultExport, 23 | namedExports, 24 | opts 25 | ) { 26 | namedExports = filterOutExportsWhichAlreadyMatchPropsOnDefault( 27 | programNode, 28 | defaultExport, 29 | namedExports 30 | ); 31 | if (!namedExports.length || opts.noExportExtend) { 32 | return { 33 | filteredExports: namedExports, 34 | newDefaultExportIdentifier: null, 35 | }; 36 | } 37 | 38 | if (namedExports.some((exp) => exp.conflict)) { 39 | return { 40 | filteredExports: namedExports, 41 | conflictingExports: namedExports.filter((exp) => exp.conflict), 42 | newDefaultExportIdentifier: null, 43 | }; 44 | } 45 | 46 | let exportVariableName = getDefaultExportName(defaultExport); 47 | let newDefaultExportIdentifier = null; 48 | if (!exportVariableName) { 49 | // Something anonymous (literal, anon function, etc...) 50 | programNode.body.push( 51 | th.buildTempExport({ 52 | VALUE: defaultExport, 53 | }) 54 | ); 55 | newDefaultExportIdentifier = th.exportsIdentifier; 56 | exportVariableName = newDefaultExportIdentifier.name; 57 | } 58 | 59 | const id = t.identifier(exportVariableName); 60 | 61 | for (const namedExport of namedExports) { 62 | programNode.body.push( 63 | th.buildAssign({ 64 | OBJECT: id, 65 | NAME: namedExport.key, 66 | VALUE: namedExport.value, 67 | }) 68 | ); 69 | } 70 | 71 | return { 72 | filteredExports: [], 73 | conflictingExports: [], 74 | newDefaultExportIdentifier: newDefaultExportIdentifier, 75 | }; 76 | } 77 | 78 | export function getDefaultExportName(defaultExport) { 79 | return defaultExport.name || ast.getIdName(defaultExport); 80 | } 81 | 82 | function filterOutExportsWhichAlreadyMatchPropsOnDefault( 83 | programNode, 84 | defaultExport, 85 | namedExports 86 | ) { 87 | namedExports = [...namedExports]; // Shallow clone for safe splicing 88 | const defaultObjectProperties = ast.findPropertiesOfNode( 89 | programNode, 90 | defaultExport 91 | ); 92 | 93 | // Loop through the default object's props and see if it already has a matching definition of the named exports. 94 | // If the definition matches, we can ignore the named export. If the definition does not match, mark it as a conflict. 95 | if (defaultObjectProperties) { 96 | for (const defaultExportProperty of defaultObjectProperties) { 97 | if (!defaultExportProperty.key) { 98 | // i.e. SpreadProperty 99 | continue; 100 | } 101 | const matchingNamedExportIndex = namedExports.findIndex( 102 | (namedExport) => namedExport.key.name === defaultExportProperty.key.name 103 | ); // get the index for splicing 104 | const matchingNamedExport = namedExports[matchingNamedExportIndex]; 105 | if (matchingNamedExport && !matchingNamedExport.conflict) { 106 | if (areSame(defaultExportProperty, matchingNamedExport)) { 107 | namedExports.splice(matchingNamedExportIndex, 1); 108 | } else { 109 | matchingNamedExport.conflict = true; 110 | } 111 | } 112 | } 113 | } 114 | 115 | return namedExports; 116 | } 117 | 118 | function areSame(defaultExportProperty, matchingNamedExport) { 119 | const defaultExportValue = defaultExportProperty.value; 120 | const declaration = matchingNamedExport.declaration; // i.e. 'export const a = 1' or 'export let a = b' 121 | if (!defaultExportValue) { 122 | // i.e. object method 123 | return false; // always a conflict 124 | } else if (t.isIdentifier(defaultExportValue)) { 125 | const valueName = defaultExportValue.name; 126 | if (valueName === matchingNamedExport.value.name) { 127 | // Same identifier reference. Safe to ignore 128 | return true; 129 | } else if ( 130 | declaration && 131 | t.isIdentifier(declaration.init) && 132 | declaration.init.name === valueName 133 | ) { 134 | // i.e. 'export let a = b', and default value for a is 'b'. 135 | return true; 136 | } 137 | } else if (t.isLiteral(defaultExportValue)) { 138 | // i.e. { a: 1 } or { a: '1' } 139 | // See if the original declaration for the matching export was for the same literal 140 | if ( 141 | t.isLiteral(declaration.init) && 142 | declaration.init.value === defaultExportValue.value 143 | ) { 144 | // i.e. 'export const a = 1' 145 | // Same literal. Safe to ignore 146 | return true; 147 | } 148 | } 149 | // Either a conflicting value or no value at all (i.e. method) 150 | return false; 151 | } 152 | -------------------------------------------------------------------------------- /packages/plugin/src/modules/helpers/wrapper.js: -------------------------------------------------------------------------------- 1 | import { types as t, traverse } from "@babel/core"; 2 | import * as eh from "./exports"; 3 | import * as th from "../../utils/templates"; 4 | import * as ast from "../../utils/ast"; 5 | 6 | export function wrap(visitor, programNode, opts) { 7 | let { 8 | defaultExport, 9 | exportGlobal, 10 | firstImportMarked, 11 | imports, 12 | namedExports, 13 | ignoredImports, 14 | injectDynamicImportHelper, 15 | } = visitor; 16 | 17 | const needsWrap = !!( 18 | defaultExport || 19 | imports.length || 20 | namedExports.length || 21 | injectDynamicImportHelper 22 | ); 23 | 24 | if (!needsWrap) { 25 | // cleanup the program node if it's empty (just having empty imports) 26 | programNode.body = programNode.body.filter((node) => { 27 | if (t.isExportNamedDeclaration(node)) { 28 | return node.declaration != null; 29 | } 30 | return true; 31 | }); 32 | return; 33 | } 34 | 35 | let { body } = programNode; 36 | 37 | // find the copyright comment from the original program body and remove it there 38 | // since it need to be put around the new program body (which is sap.ui.define) 39 | let copyright = body?.[0]?.leadingComments?.find((comment, idx, arr) => { 40 | if (comment.value.startsWith("!")) { 41 | arr.splice(idx, 1); 42 | return true; 43 | } 44 | }); 45 | 46 | // in case of TypeScript transpiling taking place upfront, the copyright comment 47 | // is associcated with the program and not the program body, so we need to find it 48 | // and move it to the new program body (which is sap.ui.define) 49 | if (!copyright) { 50 | copyright = visitor.parent?.comments?.find((comment, idx, arr) => { 51 | if (comment.value.startsWith("!")) { 52 | arr.splice(idx, 1); 53 | return true; 54 | } 55 | }); 56 | } 57 | 58 | let allExportHelperAdded = false; 59 | let extendAdded = false; 60 | 61 | opts.collapse = !opts.noExportCollapse; 62 | // opts.extend = !opts.noExportExtend 63 | 64 | // Before adding anything, see if the named exports can be collapsed into the default export. 65 | if (defaultExport && namedExports.length && opts.collapse) { 66 | let { filteredExports, conflictingExports, newDefaultExportIdentifier } = 67 | eh.collapseNamedExports(programNode, defaultExport, namedExports, opts); 68 | 69 | if (filteredExports.length && !opts.allowUnsafeMixedExports) { 70 | throw new Error( 71 | `Unsafe mixing of conflicting default and named exports. The following named exports are conflicting: (${ast 72 | .getPropNames(conflictingExports) 73 | .join(", ")}).` 74 | ); 75 | } else { 76 | namedExports = filteredExports; 77 | } 78 | 79 | // The default export may have changed if the collapse logic needed to assign a prop when the default export was previously anonymous. 80 | if (newDefaultExportIdentifier) { 81 | extendAdded = true; // If an anonymous default export needed to be assigned to a a variable, it uses the exports name for convenience. 82 | defaultExport = newDefaultExportIdentifier; 83 | } 84 | } 85 | 86 | let moveUseStrictIfNeeded = false; 87 | const preDefine = [...ignoredImports]; 88 | // If the noWrapBeforeImport opt is set, split any code before the first import and afterwards into separate arrays. 89 | // This should be done before any interops or other vars are injected. 90 | if (opts.noWrapBeforeImport && firstImportMarked) { 91 | let reachedFirstImport = false; 92 | const fullBody = body; 93 | const newBody = []; 94 | 95 | // If there is no lastBeforeWrapping, the first import is not marked 96 | if (!fullBody.find((item) => item.lastBeforeWrapping)) { 97 | reachedFirstImport = true; 98 | } 99 | 100 | for (const item of fullBody) { 101 | if (reachedFirstImport) { 102 | newBody.push(item); 103 | } else { 104 | preDefine.push(item); 105 | } 106 | if (item.lastBeforeWrapping) { 107 | reachedFirstImport = true; 108 | } 109 | } 110 | moveUseStrictIfNeeded = true; 111 | body = newBody; 112 | } 113 | 114 | // If the QUnit.config.autostart is found it needs to be moved to the top of the program 115 | if ( 116 | opts.noWrapQUnitConfigAutostart === undefined || 117 | opts.noWrapQUnitConfigAutostart 118 | ) { 119 | const qunitConfigAutostart = findQUnitConfigAutostart(body, imports); 120 | if (qunitConfigAutostart) { 121 | preDefine.push(qunitConfigAutostart); 122 | body = body.filter((node) => node !== qunitConfigAutostart); 123 | moveUseStrictIfNeeded = true; 124 | } 125 | } 126 | 127 | // if code has been moved to preDefine, we need to move the "use strict" directive 128 | if (moveUseStrictIfNeeded) { 129 | if ( 130 | !opts.neverUseStrict && 131 | preDefine.length && 132 | !hasUseStrict(programNode) 133 | ) { 134 | programNode.directives = [ 135 | t.directive(t.directiveLiteral("use strict")), 136 | ...(programNode.directives || []), 137 | ]; 138 | } 139 | } 140 | 141 | if (injectDynamicImportHelper) { 142 | // import() to sap.ui.require() w/ promise and interop 143 | body.unshift(th.buildDynamicImportHelper()); 144 | } 145 | 146 | if (!namedExports.length && defaultExport) { 147 | // If there's no named exports, return the default export 148 | body.push(t.returnStatement(defaultExport)); 149 | } else if (namedExports.length) { 150 | if (!extendAdded) { 151 | body.push(th.buildDeclareExports()); // i.e. const __exports = {__esModule: true}; 152 | } 153 | 154 | for (const namedExport of namedExports) { 155 | if (namedExport.all) { 156 | if (!allExportHelperAdded) { 157 | body.push(th.buildAllExportHelper()); 158 | allExportHelperAdded = true; 159 | } 160 | body.push( 161 | th.buildAllExport({ 162 | LOCAL: namedExport.value, 163 | }) 164 | ); 165 | } else { 166 | body.push(th.buildNamedExport(namedExport)); 167 | } 168 | } 169 | 170 | if (defaultExport) { 171 | body.push( 172 | th.buildNamedExport({ 173 | key: t.identifier("default"), 174 | value: defaultExport, 175 | }) 176 | ); 177 | } 178 | body.push(th.buildReturnExports()); 179 | } 180 | 181 | if (imports.some((imp) => imp.interop)) { 182 | body.unshift(th.buildDefaultImportInterop()); 183 | } 184 | 185 | // should we use sap.ui.require instead of sap.ui.define? 186 | let useSapUiRequire = hasUseSapUiRequire(visitor.parent.comments, body, true); 187 | 188 | // generate the sap.ui.define or sap.ui.require 189 | const defineOrRequire = generateDefineOrRequire( 190 | body, 191 | imports, 192 | exportGlobal || opts.exportAllGlobal, 193 | useSapUiRequire 194 | ); 195 | 196 | // add the "use strict" directive if not on program node 197 | if (!opts.neverUseStrict && !hasUseStrict(programNode)) { 198 | const defineOrRequireFnBody = defineOrRequire.expression.arguments[1].body; 199 | defineOrRequireFnBody.directives = [ 200 | t.directive(t.directiveLiteral("use strict")), 201 | ...(defineOrRequireFnBody.directives || []), 202 | ]; 203 | } 204 | 205 | programNode.body = [...preDefine, defineOrRequire]; 206 | 207 | // if a copyright comment is present we append it to the new program node 208 | if (copyright && visitor.parent) { 209 | visitor.parent.leadingComments = visitor.parent.leadingComments || []; 210 | visitor.parent.leadingComments.unshift(copyright); 211 | } 212 | } 213 | 214 | function hasUseStrict(node) { 215 | return (node.directives || []).some( 216 | (directive) => directive.value.value === "use strict" 217 | ); 218 | } 219 | 220 | function hasUseSapUiRequire(comments, body, remove) { 221 | // detect the @sapUiRequire comment 222 | return comments.some((comment) => { 223 | let found = false; 224 | // check for existing comment block 225 | if (comment.type === "CommentBlock") { 226 | found = comment.value.trim() === "@sapUiRequire"; 227 | } 228 | // remove the comment (if it is somewhere in the body) 229 | if (found && remove) { 230 | body?.forEach((node) => { 231 | traverse(node, { 232 | enter(path) { 233 | ["leadingComments", "trailingComments", "innerComments"].forEach( 234 | (key) => { 235 | path.node[key] = path.node[key]?.filter((c) => c !== comment); 236 | } 237 | ); 238 | }, 239 | noScope: true, 240 | }); 241 | }); 242 | } 243 | return found; 244 | }); 245 | } 246 | 247 | function findQUnitConfigAutostart(body, imports) { 248 | // if one imports QUnit, we don't need to move the QUnit.config.autostart 249 | // as the configuration should apply to the local QUnit module 250 | if (imports?.some((imp) => imp.name === "QUnit")) { 251 | return undefined; 252 | } 253 | // find the QUnit.config.autostart 254 | return body?.find((node) => { 255 | return ( 256 | t.isExpressionStatement(node) && 257 | t.isAssignmentExpression(node.expression) && 258 | t.isMemberExpression(node.expression.left) && 259 | t.isMemberExpression(node.expression.left.object) && 260 | t.isIdentifier(node.expression.left.object.object) && 261 | node.expression.left.object.object.name === "QUnit" && 262 | t.isIdentifier(node.expression.left.object.property) && 263 | node.expression.left.object.property.name === "config" && 264 | t.isIdentifier(node.expression.left.property) && 265 | node.expression.left.property.name === "autostart" && 266 | node.expression.operator === "=" /* && 267 | t.isBooleanLiteral(node.expression.right) && 268 | node.expression.right.value === false */ 269 | ); 270 | }); 271 | } 272 | 273 | function generateDefineOrRequire(body, imports, exportGlobal, useRequire) { 274 | const defineOpts = { 275 | SOURCES: t.arrayExpression(imports.map((i) => t.stringLiteral(i.src))), 276 | PARAMS: imports.map((i) => t.identifier(i.tmpName)), 277 | BODY: body, 278 | }; 279 | return useRequire 280 | ? th.buildRequire(defineOpts) 281 | : exportGlobal 282 | ? th.buildDefineGlobal(defineOpts) 283 | : th.buildDefine(defineOpts); 284 | } 285 | -------------------------------------------------------------------------------- /packages/plugin/src/modules/visitor.js: -------------------------------------------------------------------------------- 1 | import { join, dirname, resolve } from "path"; 2 | import { existsSync, statSync } from "fs"; 3 | 4 | import { types as t, template } from "@babel/core"; 5 | import * as th from "../utils/templates"; 6 | import * as ast from "../utils/ast"; 7 | 8 | import { hasJsdocGlobalExportFlag } from "../classes/helpers/jsdoc"; 9 | 10 | const resolveSource = (src, filename) => { 11 | src = src.replace(/\\/g, "/"); 12 | const dir = dirname(filename); 13 | const absoluteSrc = join(dir, src); 14 | if (existsSync(absoluteSrc) && statSync(absoluteSrc).isDirectory) { 15 | if ( 16 | existsSync(join(absoluteSrc, "index.js")) || 17 | existsSync(join(absoluteSrc, "index.jsx")) || 18 | existsSync(join(absoluteSrc, "index.ts")) || 19 | existsSync(join(absoluteSrc, "index.tsx")) || 20 | existsSync(join(absoluteSrc, "index.mjs")) || 21 | existsSync(join(absoluteSrc, "index.cjs")) 22 | ) { 23 | src = `${src}/index`; 24 | } 25 | } 26 | return src; 27 | }; 28 | const cleanImportSource = (src) => 29 | src.replace(/(\/)|(-)|(@)/g, "_").replace(/\./g, ""); 30 | const tempModuleName = (name) => `__${name}`; 31 | const hasGlobalExportFlag = (node) => hasJsdocGlobalExportFlag(node); 32 | const addImport = (imports, imp, filename, first) => { 33 | const existingImport = imports.find((i) => i.src === imp.src); 34 | if (!existingImport) { 35 | // if a module path ends with the file extension ".js" and it can be resolved to 36 | // a local file having also the file extension ".js" and not ".js.js" then 37 | // we need to slice the file extension to avoid redundant file extension 38 | // (the require/define of UI5 always adds the file extension ".js" to the module name) 39 | if (/^(?:(@[^/]+)\/)?([^/]+)\/(.*)\.js$/.test(imp.src)) { 40 | try { 41 | let modulePath; 42 | let absModuleSrc = imp.src; 43 | // if the module has a relative path, resolve it to an absolute path 44 | // and verify if the file exists, if not, try to resolve it as a module 45 | if (/^\.\.?\//.test(absModuleSrc)) { 46 | absModuleSrc = resolve(dirname(filename), absModuleSrc); 47 | // handle the fallback of module names introduced with Node 20.0.0 48 | [".js", ".jsx", ".ts", ".tsx"].some((ext) => { 49 | if (existsSync(absModuleSrc.replace(/\.js$/, ext))) { 50 | modulePath = imp.src; 51 | return true; 52 | } 53 | }); 54 | } else { 55 | modulePath = require.resolve(absModuleSrc); 56 | } 57 | // detect removal of file extension and log a hint 58 | if (modulePath.endsWith(imp.src.split("/").pop())) { 59 | console.log( 60 | `\x1b[33mHint:\x1b[0m Removed file extension for dependency "\x1b[34m${imp.src}\x1b[0m" found in \x1b[90m${filename}\x1b[0m` 61 | ); 62 | imp.src = imp.src.slice(0, -3); 63 | } 64 | } catch (ex) { 65 | // ignore the error, do not slice file extension as the module 66 | // can't be resolved with the given file extension, e.g. 67 | // myns/module.js must be provided as myns/module 68 | // myns/module.js.js must be provided as myns/module.js 69 | } 70 | } 71 | imports[first ? "unshift" : "push"](imp); 72 | } 73 | }; 74 | const addModuleImport = (imports, name, filename) => { 75 | addImport( 76 | imports, 77 | { 78 | src: name, 79 | name: name, 80 | tmpName: name, 81 | }, 82 | filename, 83 | true 84 | ); 85 | }; 86 | 87 | export const ModuleTransformVisitor = { 88 | /*! 89 | * Removes the ES6 import and adds the details to the import array in our state. 90 | */ 91 | ImportDeclaration(path, { filename, opts = {}, ...state }) { 92 | const { node } = path; 93 | 94 | if (node.importKind === "type") return; // flow-type 95 | 96 | const { specifiers, source } = node; 97 | const src = resolveSource(source.value, filename); 98 | 99 | // When 'libs' are used, only 'libs' will be converted to UI5 imports. 100 | const { libs = [".*"] } = opts; 101 | const isLibToConvert = new RegExp(`(${libs.join("|")})`).test(src); 102 | if (!isLibToConvert) { 103 | this.ignoredImports.push(node); 104 | path.remove(); 105 | return; 106 | } 107 | 108 | //const testSrc = (opts.libs || ["^sap/"]).concat(opts.files || []); 109 | // const isUi5SrcRE = testSrc.length && new RegExp(`(${testSrc.join("|")})`); 110 | // const isUi5Src = isUi5SrcRE.test(src); 111 | 112 | // Importing using an interop is the default behaviour but can be opt-out using regex. 113 | const shouldInterop = !this.noImportInteropPrefixesRegexp.test(src); 114 | 115 | const name = cleanImportSource(src); // default to the src for import without named var 116 | 117 | const { modulesMap = {} } = opts; 118 | const mappedSrc = 119 | (typeof modulesMap === "function" 120 | ? modulesMap(src, { 121 | node, 122 | opts, 123 | cwd: state.cwd, 124 | filename: state.filename, 125 | file: { 126 | opts: state.file.opts, 127 | }, 128 | }) 129 | : modulesMap[src]) || src; 130 | 131 | // Note that existingImport may get mutated if there are multiple import lines from the same module. 132 | const existingImport = this.imports.find((imp) => imp.src === mappedSrc); 133 | 134 | const imp = existingImport || { 135 | src: mappedSrc, // url 136 | name, 137 | // isLib, // for future use separating UI5 imports from npm/webpack imports 138 | // isUi5Src, // not used yet 139 | tmpName: shouldInterop ? tempModuleName(name) : name, 140 | deconstructors: [], 141 | default: false, 142 | interop: false, 143 | path: path, 144 | locked: false, 145 | }; 146 | 147 | const deconstructors = []; 148 | 149 | for (const specifier of specifiers) { 150 | if (t.isImportDefaultSpecifier(specifier)) { 151 | // e.g. import X from 'X' 152 | imp.default = true; 153 | imp.interop = shouldInterop; 154 | 155 | // Shorten the imported-as name since it should be unique for default imports. 156 | // The default import should always come first, 157 | // so this new name will be used for destructuring the other too. 158 | if (!imp.locked) { 159 | imp.name = specifier.local.name; 160 | imp.tmpName = shouldInterop ? tempModuleName(imp.name) : imp.name; 161 | imp.locked = true; 162 | } 163 | 164 | if (shouldInterop) { 165 | deconstructors.push( 166 | th.buildDefaultImportDeconstructor({ 167 | MODULE: t.identifier(imp.tmpName), 168 | LOCAL: specifier.local, 169 | }) 170 | ); 171 | } 172 | } else if (t.isImportNamespaceSpecifier(specifier)) { 173 | if (specifiers.length === 1 && !imp.locked) { 174 | // e.g. import * as X from 'X' 175 | // If the namespace specifier is the only import, we can avoid the temp name and the destructor 176 | imp.name = specifier.local.name; 177 | imp.tmpName = specifier.local.name; 178 | imp.locked = true; // Don't let another import line for the same module change the name. 179 | } else { 180 | // e.g. import X, * as X2 from 'X' 181 | // Else it's probably combined with a default export. keep the tmpName and destructure it 182 | deconstructors.push( 183 | th.buildConstDeclaration({ 184 | NAME: specifier.local, 185 | VALUE: t.identifier(imp.tmpName), 186 | }) 187 | ); 188 | } 189 | } else if (t.isImportSpecifier(specifier)) { 190 | // e.g. import { A } from 'X' 191 | deconstructors.push( 192 | th.buildNamedImportDestructor({ 193 | MODULE: t.identifier(imp.tmpName), 194 | LOCAL: specifier.local, 195 | IMPORTED: t.stringLiteral(specifier.imported.name), 196 | }) 197 | ); 198 | } else { 199 | throw path.buildCodeFrameError( 200 | `Unknown ImportDeclaration specifier type ${specifier.type}` 201 | ); 202 | } 203 | } 204 | 205 | // this is the very first import in noWrapBeforeImport mode and there are sibling nodes before this import 206 | if (opts.noWrapBeforeImport && !this.firstImportMarked && path.inList) { 207 | // for the first element there is a special case as we can't mark the previous one 208 | // therefore we do not mark anything to make clear that there's nothing to exclude 209 | if (path.key > 0) { 210 | // mark the direct predecessor as the last one to exclude from wrapping 211 | path.getSibling(path.key - 1).node.lastBeforeWrapping = true; 212 | } 213 | this.firstImportMarked = true; 214 | } 215 | 216 | path.replaceWithMultiple(deconstructors); 217 | 218 | if (deconstructors.length) { 219 | // Keep the same variable name if the same module is imported on another line. 220 | imp.locked = true; 221 | } 222 | 223 | imp.deconstructors = imp.deconstructors.concat(deconstructors); 224 | 225 | if (!existingImport) { 226 | addImport(this.imports, imp, filename); 227 | } 228 | }, 229 | 230 | /** 231 | * Push all exports to an array. 232 | * The reason we don't export in place is to handle the situation 233 | * where a let or var can be defined, and the latest one should be exported. 234 | */ 235 | ExportNamedDeclaration(path, { filename }) { 236 | const { node } = path; 237 | const { specifiers, declaration, source } = node; 238 | 239 | let fromSource = ""; 240 | if (source) { 241 | // e.g. export { one, two } from 'x' 242 | const src = resolveSource(source.value, filename); 243 | const name = cleanImportSource(src); 244 | const tmpName = tempModuleName(name); 245 | addImport(this.imports, { src, name, tmpName }, filename); 246 | fromSource = tmpName + "."; 247 | } 248 | 249 | if (specifiers && specifiers.length) { 250 | // e.g. export { one, two } 251 | for (const specifier of path.node.specifiers) { 252 | this.namedExports.push({ 253 | key: specifier.exported, 254 | value: t.identifier(`${fromSource}${specifier.local.name}`), 255 | }); 256 | } 257 | path.remove(); 258 | } else if (declaration) { 259 | // e.g. export const c = 1 | export function f() {} 260 | if ( 261 | [ 262 | "TypeAlias", 263 | "InterfaceDeclaration", 264 | "TSInterfaceDeclaration", 265 | "TSTypeAliasDeclaration", 266 | ].includes(declaration.type) 267 | ) 268 | return; // TS or Flow-types 269 | const name = ast.getIdName(declaration); 270 | if (name) { 271 | // e.g. export function f() {} 272 | const id = t.identifier(declaration.id.name); 273 | this.namedExports.push({ 274 | key: id, 275 | value: id, 276 | declaration, 277 | }); 278 | } else if (declaration.declarations) { 279 | // e.g. export const c = 1 280 | for (const subDeclaration of declaration.declarations) { 281 | const id = t.identifier(subDeclaration.id.name); 282 | this.namedExports.push({ 283 | value: id, 284 | key: id, 285 | declaration: subDeclaration, 286 | }); 287 | } 288 | } else { 289 | throw path.buildCodeFrameError("Unknown ExportNamedDeclaration shape."); 290 | } 291 | path.replaceWith(declaration); 292 | } else { 293 | path.remove(); 294 | return; 295 | } 296 | }, 297 | 298 | ExportDefaultDeclaration(path /*, { filename } */) { 299 | const { node } = path; 300 | let { declaration } = node; 301 | const declarationName = ast.getIdName(declaration); 302 | if (hasGlobalExportFlag(node)) { 303 | // check for jsdoc @export 304 | this.exportGlobal = true; 305 | } 306 | if (declarationName) { 307 | // ClassDeclaration or FunctionDeclaration with name. 308 | // Leave the declaration in-line and preserve the identifier for the return statement. 309 | path.replaceWith(declaration); 310 | this.defaultExport = t.identifier(declarationName); 311 | } else if (t.isIdentifier(declaration)) { 312 | this.defaultExport = declaration; 313 | path.remove(); 314 | } else { 315 | // anonymous ObjectExpression or anonymous FunctionDeclaration 316 | if (t.isFunctionDeclaration(declaration)) { 317 | const { params, body, generator, async: isAsync } = declaration; 318 | declaration = t.functionExpression( 319 | null, 320 | params, 321 | body, 322 | generator, 323 | isAsync 324 | ); 325 | } 326 | const exportDeclaration = th.buildTempExport({ 327 | VALUE: declaration, 328 | }); 329 | path.replaceWith(exportDeclaration); 330 | this.defaultExport = th.exportsIdentifier; 331 | } 332 | }, 333 | 334 | ExportAllDeclaration(path, { filename }) { 335 | const src = resolveSource(path.node.source.value, filename); 336 | const name = cleanImportSource(src); 337 | const tmpName = tempModuleName(name); 338 | 339 | addImport(this.imports, { src, name, tmpName }, filename); 340 | 341 | this.exportAllHelper = true; 342 | 343 | this.namedExports.push({ 344 | all: true, 345 | value: t.identifier(tmpName), 346 | }); 347 | 348 | path.remove(); 349 | }, 350 | 351 | /*! 352 | * Visits function calls to handle for dynamic imports. 353 | */ 354 | CallExpression(path) { 355 | const { node } = path; 356 | const { callee } = node; 357 | if (ast.isImport(callee)) { 358 | this.injectDynamicImportHelper = true; 359 | path.replaceWith({ 360 | ...node, 361 | callee: t.identifier("__ui5_require_async"), 362 | }); 363 | } 364 | }, 365 | 366 | MemberExpression(path, { filename }) { 367 | const { node } = path; 368 | if (node?.object?.type === "MetaProperty") { 369 | // replace all "import.meta.url" with "module.url" 370 | if (node?.property?.name === "url") { 371 | path.replaceWith({ 372 | ...node, 373 | ...template`module.url`(), 374 | }); 375 | addModuleImport(this.imports, "module", filename); 376 | } 377 | // replace all "import.meta.resolve(...)" with "require.toUrl(...)" 378 | else if (node?.property?.name === "resolve") { 379 | path.replaceWith({ 380 | ...node, 381 | ...template`require.toUrl`(), 382 | }); 383 | addModuleImport(this.imports, "require", filename); 384 | } 385 | } 386 | }, 387 | }; 388 | -------------------------------------------------------------------------------- /packages/plugin/src/utils/ast.js: -------------------------------------------------------------------------------- 1 | import { types as t } from "@babel/core"; 2 | import { flatten } from "array-flatten"; 3 | 4 | export function isObjectAssignOrExtendsExpression(node) { 5 | return isObjectAssignExpression(node) || isExtendsHelperExpression(node); 6 | } 7 | 8 | export function isObjectAssignExpression(node) { 9 | if (!t.isCallExpression(node)) return false; 10 | const callee = node && node.callee; 11 | return !!( 12 | callee.object && 13 | callee.property && 14 | callee.object.name === "Object" && 15 | callee.property.name === "assign" 16 | ); 17 | } 18 | 19 | export function isExtendsHelperExpression(node) { 20 | if (!t.isCallExpression(node)) return false; 21 | const callee = node && node.callee; 22 | return isIdentifierNamed(callee, "_extends"); 23 | } 24 | 25 | // t.isImport only exists if the dynamic import syntax plugin is used, so avoid using that to make it optional. 26 | export function isImport(node) { 27 | return node && node.type === "Import"; 28 | } 29 | 30 | export function isIdentifierNamed(node, name) { 31 | return t.isIdentifier(node, { name: name }); 32 | } 33 | 34 | // This doesn't exist on babel-types 35 | export function isCommentBlock(node) { 36 | return node && node.type === "CommentBlock"; 37 | } 38 | 39 | export function getIdName(node) { 40 | return node.id && node.id.name; 41 | } 42 | 43 | export function isSuperCallExpression(expression) { 44 | return t.isCallExpression(expression) && expression.callee.type === "Super"; 45 | } 46 | 47 | // export function isSuperExpressionStatement(node) { 48 | // return isSuperCallExpression(node.expression) 49 | // } 50 | 51 | export function findPropertiesOfNode(blockScopeNode, declaration) { 52 | if ( 53 | t.isFunctionDeclaration(declaration) || 54 | t.isArrowFunctionExpression(declaration) 55 | ) { 56 | return null; 57 | } else if (t.isObjectExpression(declaration)) { 58 | return declaration.properties; 59 | } else if (t.isClass(declaration)) { 60 | return [ 61 | ...getInternalStaticThingsOfClass(declaration), 62 | ...getOtherPropertiesOfIdentifier(blockScopeNode, declaration.id.name), 63 | ]; 64 | } else if (t.isIdentifier(declaration)) { 65 | return getOtherPropertiesOfIdentifier(blockScopeNode, declaration.name); 66 | } else if (isObjectAssignOrExtendsExpression(declaration)) { 67 | return getPropertiesOfObjectAssignOrExtendHelper( 68 | declaration, 69 | blockScopeNode 70 | ); 71 | } 72 | return null; 73 | } 74 | 75 | export function getInternalStaticThingsOfClass(classNode) { 76 | return classNode.body.body.filter((item) => item.static); 77 | } 78 | 79 | /** 80 | * Traverse the top-level of the scope (program or function body) looking for either: 81 | * - ObjectExpression assignments to the object variable. 82 | * - Property assignments directly to our object (but not to nested properties). 83 | * 84 | * @return Array<{key, value}> 85 | */ 86 | export function getOtherPropertiesOfIdentifier(blockScopeNode, idName) { 87 | return flatten( 88 | blockScopeNode.body 89 | .map((node) => { 90 | if (t.isExpressionStatement(node)) { 91 | // ID = value | ID.key = value | ID.key.nested = value 92 | const { left, right } = node.expression; 93 | if (t.isAssignmentExpression(node.expression)) { 94 | if (t.isIdentifier(left) && left.name === idName) { 95 | // ID = value 96 | if (t.isObjectExpression(right)) { 97 | // ID = {} 98 | return right.properties; // Array 99 | } 100 | } else { 101 | const { object, property: key } = left; 102 | if (t.isIdentifier(object) && object.name === idName) { 103 | // ID.key = value 104 | return { key, value: right }; // ObjectProperty-like (key, value) 105 | } 106 | } 107 | } 108 | } else if (t.isVariableDeclaration(node)) { 109 | return node.declarations 110 | .filter((declaration) => declaration.id.name === idName) 111 | .map((declaration) => declaration.init) 112 | .filter((init) => init) 113 | .filter( 114 | (init) => 115 | t.isObjectExpression(init) || 116 | isObjectAssignOrExtendsExpression(init) 117 | ) 118 | .map((init) => 119 | t.isObjectExpression(init) 120 | ? init.properties 121 | : getPropertiesOfObjectAssignOrExtendHelper( 122 | init, 123 | blockScopeNode 124 | ) 125 | ); 126 | } 127 | }) 128 | .filter((item) => item) 129 | ); 130 | } 131 | 132 | export function getPropertiesOfObjectAssignOrExtendHelper( 133 | node, 134 | blockScopeNode 135 | ) { 136 | // Check all the args and recursively try to get props of identifiers (although they may be imported) 137 | return flatten( 138 | node.arguments.map((arg) => { 139 | if (t.isObjectExpression(arg)) { 140 | return arg.properties; 141 | } else if (t.isIdentifier(arg)) { 142 | // Recursive, although props will be empty if arg is an imported object 143 | return getOtherPropertiesOfIdentifier(blockScopeNode, arg.name); 144 | } 145 | }) 146 | ); 147 | } 148 | 149 | export function getPropNames(props) { 150 | return props.map((prop) => prop.key.name); 151 | } 152 | 153 | export function groupPropertiesByName(properties) { 154 | return ( 155 | properties && 156 | properties.reduce((accumulator, property) => { 157 | accumulator[property.key.name] = property.value; 158 | return accumulator; 159 | }, {}) 160 | ); 161 | } 162 | 163 | export function convertFunctionDeclarationToExpression(declaration) { 164 | return t.functionExpression( 165 | declaration.id, 166 | declaration.params, 167 | declaration.body, 168 | declaration.generator, 169 | declaration.async 170 | ); 171 | } 172 | 173 | export function convertDeclarationToExpression(declaration) { 174 | if (t.isFunctionDeclaration(declaration)) { 175 | return convertFunctionDeclarationToExpression(declaration); 176 | } else { 177 | return declaration; 178 | } 179 | } 180 | 181 | export function isSuperPrototypeCallOf( 182 | expression, 183 | superClassName, 184 | superMethodName 185 | ) { 186 | return ( 187 | t.isCallExpression(expression) && 188 | (isCallExpressionCalling( 189 | expression, 190 | `${superClassName}.prototype.${superMethodName}.apply` 191 | ) || 192 | isCallExpressionCalling( 193 | expression, 194 | `${superClassName}.prototype.${superMethodName}.call` 195 | )) 196 | ); 197 | } 198 | 199 | /** 200 | * Helper to see if a call expression is calling a given method such as sap.ui.define(). 201 | * The AST is structured in reverse (defined > ui > sap) so we reverse the method call to compare. 202 | * 203 | * @param {CallExpression} expression 204 | * @param {String} dotNotationString For example, sap.ui.define or Class.prototype.method.apply 205 | */ 206 | export function isCallExpressionCalling(expression, dotNotationString) { 207 | if (!t.isCallExpression(expression)) return false; 208 | const callee = expression.callee; 209 | if (callee.type === "Identifier") { 210 | return callee.name === dotNotationString; 211 | } else { 212 | const parts = dotNotationString.split("."); 213 | let node = callee; 214 | for (const nextNamePart of parts.reverse()) { 215 | if (!node) return false; 216 | // property won't be there for an anonymous function 217 | const nodeName = node.name || (node.property && node.property.name); 218 | if (nodeName !== nextNamePart) return false; 219 | node = node.object; 220 | } 221 | return true; 222 | } 223 | } 224 | 225 | /** 226 | * Recursively search through some parts of a node's for use of 'this'. 227 | * It checks the callee and the arguments and traverses into arrow functions. 228 | * 229 | * True scenarios include: 230 | * - this (ThisExpression) 231 | * - this.a.b (MemberExpression) 232 | * - this.thing() (CallExpression > callee > MemberExpression > ThisExpression) 233 | * - method(this) (CallExpression > arguments > ThisExpression) 234 | * - method(this.a) (CallExpression > arguments > MemberExpression > ThisExpression) 235 | * - () => this 236 | * - () => this() 237 | * - () => fn(this) 238 | * - fn1(() => this) 239 | * - fn1(() => this()) 240 | * - fn1(() => fn(this)) 241 | * 242 | * Exits w/ false if a non-arrow function is encountered even if 'this' is used within. 243 | * - fn1(function() { this.fn2(); }) 244 | * 245 | * @param {*} node 246 | */ 247 | export function isThisExpressionUsed(node) { 248 | if (!node) { 249 | return false; 250 | } 251 | if (Array.isArray(node)) { 252 | return node.some(isThisExpressionUsed); 253 | } 254 | if (t.isThisExpression(node)) { 255 | return true; 256 | } 257 | if (t.isFunctionExpression(node)) { 258 | return false; 259 | } 260 | const traversableProps = [ 261 | "body", 262 | "callee", 263 | "arguments", 264 | "expression", 265 | "object", 266 | "property", 267 | ]; 268 | return traversableProps.some( 269 | (prop) => node[prop] && isThisExpressionUsed(node[prop]) 270 | ); 271 | } 272 | -------------------------------------------------------------------------------- /packages/plugin/src/utils/templates.js: -------------------------------------------------------------------------------- 1 | import { template, types as t } from "@babel/core"; 2 | 3 | const exportName = "__exports"; 4 | 5 | export const exportsIdentifier = t.identifier(exportName); 6 | 7 | export const buildAssign = template(` 8 | OBJECT.NAME = VALUE; 9 | `); 10 | 11 | export const buildRequire = template(` 12 | sap.ui.require(SOURCES, function (PARAMS) { 13 | BODY; 14 | }); 15 | `); 16 | 17 | export const buildDefine = template(` 18 | sap.ui.define(SOURCES, function (PARAMS) { 19 | BODY; 20 | }); 21 | `); 22 | 23 | export const buildDefineGlobal = template(` 24 | sap.ui.define(SOURCES, function (PARAMS) { 25 | BODY; 26 | }, true); 27 | `); 28 | 29 | // Uses 'var' since it gets added during wrap 30 | export const buildDeclareExports = template(` 31 | var ${exportName} = { 32 | __esModule: true 33 | }; 34 | `); 35 | 36 | // Uses 'var' since it gets added during wrap 37 | export const buildTempExport = template(` 38 | var ${exportName} = VALUE; 39 | `); 40 | 41 | export const buildExportDefault = template(` 42 | export default VALUE; 43 | `); 44 | 45 | export const buildReturnExports = template(` 46 | return ${exportName}; 47 | `); 48 | 49 | export function buildNamedExport(obj) { 50 | return buildAssign({ 51 | OBJECT: exportsIdentifier, 52 | NAME: obj.key, 53 | VALUE: obj.value, 54 | }); 55 | } 56 | 57 | export const buildAllExportHelper = template(` 58 | function extendExports(exports, obj) { 59 | obj && Object.keys(obj).forEach(function (key) { 60 | if (key === "default" || key === "__esModule") return; 61 | Object.defineProperty(exports, key, { 62 | enumerable: true, 63 | get: function get() { 64 | return obj[key]; 65 | } 66 | }); 67 | }); 68 | } 69 | `); 70 | 71 | export const buildAllExport = template(` 72 | extendExports(${exportName}, LOCAL); 73 | `); 74 | 75 | export const buildReturn = template(` 76 | return ID; 77 | `); 78 | 79 | export const buildDefaultImportInterop = template(` 80 | function _interopRequireDefault(obj) { return (obj && obj.__esModule && typeof obj.default !== "undefined") ? obj.default : obj; } 81 | `); 82 | 83 | export const buildDefaultImportDeconstructor = template(` 84 | const LOCAL = _interopRequireDefault(MODULE); 85 | `); 86 | 87 | /* 88 | * Scenarios for dynamic exports: 89 | * 90 | * 1.) ES module with no exports 91 | * 92 | * (await import("./moduleWithNoExports.js")) ==> Module 93 | * (await import("./moduleWithNoExports.js")).default ==> undefined 94 | * 95 | * 2.) ES module with default export 96 | * 97 | * (await import("./moduleWithDefaultExport.js")) ==> Module 98 | * (await import("./moduleWithDefaultExport.js")).default ==> DefaultExport 99 | * 100 | * 3.) ES module with named exports 101 | * 102 | * (await import("./moduleWithNamedExport.js")) ==> Module 103 | * (await import("./moduleWithNamedExport.js")).default ==> undefined 104 | * (await import("./moduleWithNamedExport.js")).NamedExport ==> NamedExport 105 | * 106 | * A Module object is always returned by the dynamic import. 107 | * 108 | * --- 109 | * 110 | * The dynamic import script template simulates the default and named exports 111 | * for non-ES modules so that the TypeScript code completion can be properly 112 | * used. The template below is used to translate dynamic imports to sap.ui.require 113 | * calls which are typically be used for the following scenarios: 114 | * 115 | * 1.) UI5 Libraries 116 | * 117 | * A UI5 library exports its complete namespace and all available 118 | * types/enums/... are accessible as named properties on that 119 | * namespace, e.g.: 120 | * 121 | * (await import("sap/m/library")) => { __esModule: true, ... } 122 | * (await import("sap/m/library")).default => undefined 123 | * (await import("sap/m/library")).ButtonType => sap/m/ButtonType 124 | * 125 | * 2.) UI5 Classes 126 | * 127 | * A UI5 class exports itself as default. When importing a UI5 class 128 | * it is expected to be accessible via the default property. 129 | * 130 | * (await import("sap/m/Button")) => { __esModule: true, ... } 131 | * (await import("sap/m/Button")).default => sap/m/Button 132 | * 133 | * 3.) Static Helpers (sap/m/MessageBox) 134 | * 135 | * (await import("sap/m/MessageBox")) => { __esModule: true, ... } 136 | * (await import("sap/m/MessageBox")).default => sap/m/MessageBox 137 | * (await import("sap/m/MessageBox")).Action => undefined 138 | * (await import("sap/m/MessageBox")).default.Action => sap/m/MessageBox.Action 139 | * 140 | * 4.) undefined or null (no exports) 141 | * 142 | * (await import("./non/exporting/module")) => { __esModule: true, ... } 143 | * (await import("./non/exporting/module")) => undefined 144 | * 145 | * 5.) Other dependencies (jsPDF) 146 | * 147 | * (await import("jsPDF")) => { __esModule: true, jsPDF: ?, ... } 148 | * (await import("sap/m/MessageBox")).jsPDF => jsPDF 149 | * 150 | * When requiring other dependencies they are already flagged as __esModule 151 | * and must not be processed in our dynamic import to require handler. 152 | */ 153 | // TODO: inject __extends instead of Object.assign unless useBuiltIns in set 154 | export const buildDynamicImportHelper = template(` 155 | function __ui5_require_async(path) { 156 | return new Promise(function(resolve, reject) { 157 | sap.ui.require([path], function(module) { 158 | if (!(module && module.__esModule)) { 159 | module = module === null || !(typeof module === "object" && path.endsWith("/library")) ? { default: module } : module; 160 | Object.defineProperty(module, "__esModule", { value: true }); 161 | } 162 | resolve(module); 163 | }, function(err) { 164 | reject(err); 165 | }); 166 | }); 167 | } 168 | `); 169 | 170 | export const buildConstDeclaration = template(` 171 | const NAME = VALUE; 172 | `); 173 | 174 | export const buildNamedImportDestructor = template(` 175 | const LOCAL = MODULE[IMPORTED]; 176 | `); 177 | 178 | export const buildExtendAssign = template(` 179 | const NAME = SUPER.extend(FQN, OBJECT); 180 | `); 181 | 182 | // This is use when there is not already the function, so always propagate arguments. 183 | export const buildInheritingFunction = template(` 184 | function NAME() { 185 | if (typeof SUPER.prototype.NAME === 'function') { 186 | SUPER.prototype.NAME.apply(this, arguments); 187 | } 188 | } 189 | `); 190 | 191 | // This is use when there is not already the function, so always propagate arguments. 192 | export const buildInheritingConstructor = template(` 193 | function constructor() { 194 | SUPER.prototype.constructor.apply(this, arguments); 195 | } 196 | `); 197 | -------------------------------------------------------------------------------- /packages/preset/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [], 3 | "presets": [ 4 | ["@babel/preset-env", { 5 | "targets": { 6 | "node": "6.10" 7 | } 8 | }] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/preset/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .babelrc 3 | .eslintignore 4 | -------------------------------------------------------------------------------- /packages/preset/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [7.7.1](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.7.0...v7.7.1) (2025-05-13) 7 | 8 | **Note:** Version bump only for package babel-preset-transform-ui5 9 | 10 | 11 | 12 | 13 | 14 | # [7.7.0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.6.2...v7.7.0) (2024-09-16) 15 | 16 | 17 | ### Features 18 | 19 | * update dependencies / add peer dependency to @babel/core ([#135](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/135)) ([d155e48](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/d155e48a4a3737ebcf4a7e77318391bbce783e70)), closes [#133](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/133) 20 | 21 | 22 | 23 | 24 | 25 | ## [7.6.2](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.6.1...v7.6.2) (2024-09-14) 26 | 27 | **Note:** Version bump only for package babel-preset-transform-ui5 28 | 29 | 30 | 31 | 32 | 33 | ## [7.6.1](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.6.0...v7.6.1) (2024-09-13) 34 | 35 | **Note:** Version bump only for package babel-preset-transform-ui5 36 | 37 | 38 | 39 | 40 | 41 | # [7.6.0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.5.0...v7.6.0) (2024-07-22) 42 | 43 | **Note:** Version bump only for package babel-preset-transform-ui5 44 | 45 | 46 | 47 | 48 | 49 | # [7.5.0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.4.2...v7.5.0) (2024-07-12) 50 | 51 | **Note:** Version bump only for package babel-preset-transform-ui5 52 | 53 | 54 | 55 | 56 | 57 | ## [7.4.2](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.4.1...v7.4.2) (2024-06-25) 58 | 59 | **Note:** Version bump only for package babel-preset-transform-ui5 60 | 61 | 62 | 63 | 64 | 65 | ## [7.4.1](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.4.0...v7.4.1) (2024-06-02) 66 | 67 | **Note:** Version bump only for package babel-preset-transform-ui5 68 | 69 | 70 | 71 | 72 | 73 | # [7.4.0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.3.1...v7.4.0) (2024-05-31) 74 | 75 | **Note:** Version bump only for package babel-preset-transform-ui5 76 | 77 | 78 | 79 | 80 | 81 | ## [7.3.1](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.3.0...v7.3.1) (2024-05-12) 82 | 83 | **Note:** Version bump only for package babel-preset-transform-ui5 84 | 85 | 86 | 87 | 88 | 89 | # [7.3.0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.2.6...v7.3.0) (2024-01-18) 90 | 91 | **Note:** Version bump only for package babel-preset-transform-ui5 92 | 93 | 94 | 95 | 96 | 97 | ## [7.2.6](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.2.5...v7.2.6) (2023-11-23) 98 | 99 | **Note:** Version bump only for package babel-preset-transform-ui5 100 | 101 | 102 | 103 | 104 | 105 | ## [7.2.5](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.2.4...v7.2.5) (2023-08-28) 106 | 107 | **Note:** Version bump only for package babel-preset-transform-ui5 108 | 109 | 110 | 111 | 112 | 113 | ## [7.2.4](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.2.3...v7.2.4) (2023-07-20) 114 | 115 | **Note:** Version bump only for package babel-preset-transform-ui5 116 | 117 | 118 | 119 | 120 | 121 | ## [7.2.3](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.2.2...v7.2.3) (2023-07-18) 122 | 123 | **Note:** Version bump only for package babel-preset-transform-ui5 124 | 125 | 126 | 127 | 128 | 129 | ## [7.2.2](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.2.1...v7.2.2) (2023-07-18) 130 | 131 | **Note:** Version bump only for package babel-preset-transform-ui5 132 | 133 | 134 | 135 | 136 | 137 | ## [7.2.1](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.2.0...v7.2.1) (2023-06-13) 138 | 139 | **Note:** Version bump only for package babel-preset-transform-ui5 140 | 141 | 142 | 143 | 144 | 145 | # [7.2.0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.1.5...v7.2.0) (2023-05-30) 146 | 147 | 148 | ### Features 149 | 150 | * split class conversion to enable other Babel plugin conversion ([#100](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/100)) ([4ba096b](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/commit/4ba096b1a24d807cda2fd2f57425f3ab4b91a31b)), closes [#23](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/23) [#25](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/issues/25) 151 | 152 | 153 | 154 | 155 | 156 | ## [7.1.5](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.1.4...v7.1.5) (2023-05-30) 157 | 158 | **Note:** Version bump only for package babel-preset-transform-ui5 159 | 160 | 161 | 162 | 163 | 164 | ## [7.1.4](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.1.3...v7.1.4) (2023-04-11) 165 | 166 | **Note:** Version bump only for package babel-preset-transform-ui5 167 | 168 | 169 | 170 | 171 | 172 | ## [7.1.3](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.1.2...v7.1.3) (2023-04-11) 173 | 174 | **Note:** Version bump only for package babel-preset-transform-ui5 175 | 176 | 177 | 178 | 179 | 180 | ## [7.1.2](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.1.1...v7.1.2) (2023-04-11) 181 | 182 | **Note:** Version bump only for package babel-preset-transform-ui5 183 | 184 | 185 | 186 | 187 | 188 | ## [7.1.1](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.1.0...v7.1.1) (2023-03-27) 189 | 190 | **Note:** Version bump only for package babel-preset-transform-ui5 191 | 192 | 193 | 194 | 195 | 196 | # [7.1.0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.0.6...v7.1.0) (2023-03-13) 197 | 198 | **Note:** Version bump only for package babel-preset-transform-ui5 199 | 200 | 201 | 202 | 203 | 204 | ## [7.0.6](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.0.5...v7.0.6) (2023-02-21) 205 | 206 | **Note:** Version bump only for package babel-preset-transform-ui5 207 | -------------------------------------------------------------------------------- /packages/preset/README.md: -------------------------------------------------------------------------------- 1 | 2 | # babel-preset-transform-ui5 3 | 4 | [Docs](../../README.md) 5 | 6 | ## Install 7 | 8 | ```sh 9 | npm install babel-preset-transform-ui5 --save-dev 10 | ``` 11 | 12 | or 13 | 14 | ```sh 15 | yarn add babel-preset-transform-ui5 --dev 16 | ``` 17 | -------------------------------------------------------------------------------- /packages/preset/index.js: -------------------------------------------------------------------------------- 1 | const transformUI5 = require("babel-plugin-transform-modules-ui5"); 2 | 3 | module.exports = (context, opts = {}) => ({ 4 | plugins: [[transformUI5, opts]], 5 | }); 6 | -------------------------------------------------------------------------------- /packages/preset/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-preset-transform-ui5", 3 | "version": "7.7.1", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/ui5-community/babel-plugin-transform-modules-ui5.git", 9 | "directory": "packages/preset" 10 | }, 11 | "dependencies": { 12 | "babel-plugin-transform-modules-ui5": "^7.7.1" 13 | }, 14 | "devDependencies": { 15 | "prettier": "^3.3.3" 16 | }, 17 | "scripts": { 18 | "format": "prettier --write index.js" 19 | }, 20 | "keywords": [ 21 | "ui5", 22 | "sap", 23 | "sapui5", 24 | "openui5", 25 | "babel", 26 | "babel7", 27 | "babeljs", 28 | "es6", 29 | "module", 30 | "import", 31 | "export", 32 | "typescript", 33 | "ts", 34 | "preset" 35 | ] 36 | } 37 | --------------------------------------------------------------------------------