babel-plugin-transform-modules-ui5 7.4.2 → 7.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,28 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [7.6.0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.5.0...v7.6.0) (2024-07-22)
7
+
8
+
9
+ ### Features
10
+
11
+ * 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))
12
+
13
+
14
+
15
+
16
+
17
+ # [7.5.0](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.4.2...v7.5.0) (2024-07-12)
18
+
19
+
20
+ ### Features
21
+
22
+ * 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))
23
+
24
+
25
+
26
+
27
+
6
28
  ## [7.4.2](https://github.com/ui5-community/babel-plugin-transform-modules-ui5/compare/v7.4.1...v7.4.2) (2024-06-25)
7
29
 
8
30
 
@@ -121,6 +121,7 @@ function convertClassToUI5Extend(path, node, classInfo, extraStaticProps, import
121
121
  const typeAnnotation = (_member$typeAnnotatio = member.typeAnnotation) === null || _member$typeAnnotatio === void 0 ? void 0 : _member$typeAnnotatio.typeAnnotation;
122
122
  // double-check that it is a valid node for a controller extension
123
123
  if (_core.types.isTSTypeReference(typeAnnotation) || _core.types.isTSQualifiedName(typeAnnotation)) {
124
+ var _memberPath$hub;
124
125
  const typeName = getTypeName(typeAnnotation);
125
126
 
126
127
  // 1. transform the property from being typed as instance and un-initialized to a property where the controller extension *class* is assigned as value
@@ -137,7 +138,7 @@ function convertClassToUI5Extend(path, node, classInfo, extraStaticProps, import
137
138
  }
138
139
 
139
140
  // 3. restore the import in case it was run already and removed the import
140
- const neededImportDeclaration = (0, _imports.getImportDeclaration)(memberPath.hub.file.opts.filename, typeName);
141
+ const neededImportDeclaration = (0, _imports.getImportDeclaration)(memberPath === null || memberPath === void 0 || (_memberPath$hub = memberPath.hub) === null || _memberPath$hub === void 0 || (_memberPath$hub = _memberPath$hub.file) === null || _memberPath$hub === void 0 || (_memberPath$hub = _memberPath$hub.opts) === null || _memberPath$hub === void 0 ? void 0 : _memberPath$hub.filename, typeName);
141
142
  if (!importDeclarationPaths.some(path => path.node === neededImportDeclaration)) {
142
143
  // TODO: import might be there but with the specifier removed; we can clone, but then other specifiers are duplicate
143
144
  // if import is no longer there, re-add it
@@ -150,6 +151,36 @@ function convertClassToUI5Extend(path, node, classInfo, extraStaticProps, import
150
151
  }
151
152
  if (!member.value) continue; // remove all other un-initialized static class props (typescript)
152
153
 
154
+ // Now handle the case where an extended controller extension - not just e.g. "Routing", but
155
+ // "Routing.override({...})" - is assigned to the class property. To make this work in TypeScript code
156
+ // (Routing.override({...}) returns a class, but the "routing" member property needs to be typed as an instance),
157
+ // we require the following notation, using a dummy function ControllerExtension.use() that does actually
158
+ // not exist at runtime, but does the required class-to-instance conversion from TypeScript perspective:
159
+ // routing: ControllerExtension.use(Routing.override({...}))
160
+ // In this case, the code in the "if" clause above was not executed, regardless of the presence of the
161
+ // @transformControllerExtension marker, because it is not a type assignment. In the resulting code, the
162
+ // "ControllerExtension.use(...)" part should be removed and the content of the brackets should be assigned
163
+ // directly to the member property.
164
+ if (_core.types.isCallExpression(member.value)) {
165
+ const callee = member.value.callee;
166
+ if (_core.types.isMemberExpression(callee) && _core.types.isIdentifier(callee.object) && _core.types.isIdentifier(callee.property) && callee.property.name === "use" // we are looking for "ControllerExtension.use(...)"
167
+ ) {
168
+ var _memberPath$hub2, _callee$object, _importDeclaration$so;
169
+ const importDeclaration = (0, _imports.getImportDeclaration)(memberPath === null || memberPath === void 0 || (_memberPath$hub2 = memberPath.hub) === null || _memberPath$hub2 === void 0 || (_memberPath$hub2 = _memberPath$hub2.file) === null || _memberPath$hub2 === void 0 || (_memberPath$hub2 = _memberPath$hub2.opts) === null || _memberPath$hub2 === void 0 ? void 0 : _memberPath$hub2.filename, callee === null || callee === void 0 || (_callee$object = callee.object) === null || _callee$object === void 0 ? void 0 : _callee$object.name // usually, but not necessarily always: "ControllerExtension"...
170
+ );
171
+ // ...hence we rather look at the imported module name to be sure
172
+ if ((importDeclaration === null || importDeclaration === void 0 || (_importDeclaration$so = importDeclaration.source) === null || _importDeclaration$so === void 0 ? void 0 : _importDeclaration$so.value) === "sap/ui/core/mvc/ControllerExtension") {
173
+ if (!member.value.arguments || member.value.arguments.length !== 1) {
174
+ // exactly one argument must be there
175
+ throw memberPath.buildCodeFrameError(`ControllerExtension.use() must be called with exactly one argument but has ${member.value.arguments ? member.value.arguments.length : 0}`);
176
+ }
177
+ member.value = member.value.arguments[0];
178
+ extendProps.unshift(buildObjectProperty(member)); // add it to the properties of the extend() config object
179
+ continue; // prevent the member from also being added to the constructor
180
+ }
181
+ }
182
+ }
183
+
153
184
  // Special handling for TypeScript limitation where metadata, renderer and overrides must be properties.
154
185
  if (["metadata", "renderer", "overrides"].includes(memberName)) {
155
186
  if (opts.overridesToOverride && member.key.name === "overrides") {
@@ -15,6 +15,9 @@ const saveImports = file => {
15
15
  // can be called from visitor to access previously present declarations
16
16
  exports.saveImports = saveImports;
17
17
  function getImportDeclaration(filename, typeName) {
18
+ if (!filename || !typeName) {
19
+ return null;
20
+ }
18
21
  const typeNameParts = typeName.split(".");
19
22
 
20
23
  // find the declaration importing the typeName among the collected import declarations in this file
@@ -139,14 +139,19 @@ function wrap(visitor, programNode, opts) {
139
139
  if (imports.some(imp => imp.interop)) {
140
140
  body.unshift(th.buildDefaultImportInterop());
141
141
  }
142
- const define = generateDefine(body, imports, exportGlobal || opts.exportAllGlobal, hasUseStrict(programNode));
142
+
143
+ // should we use sap.ui.require instead of sap.ui.define?
144
+ let useSapUiRequire = hasUseSapUiRequire(visitor.parent.comments, body, true);
145
+
146
+ // generate the sap.ui.define or sap.ui.require
147
+ const defineOrRequire = generateDefineOrRequire(body, imports, exportGlobal || opts.exportAllGlobal, useSapUiRequire);
143
148
 
144
149
  // add the "use strict" directive if not on program node
145
150
  if (!opts.neverUseStrict && !hasUseStrict(programNode)) {
146
- const defineFnBody = define.expression.arguments[1].body;
147
- defineFnBody.directives = [_core.types.directive(_core.types.directiveLiteral("use strict")), ...(defineFnBody.directives || [])];
151
+ const defineOrRequireFnBody = defineOrRequire.expression.arguments[1].body;
152
+ defineOrRequireFnBody.directives = [_core.types.directive(_core.types.directiveLiteral("use strict")), ...(defineOrRequireFnBody.directives || [])];
148
153
  }
149
- programNode.body = [...preDefine, define];
154
+ programNode.body = [...preDefine, defineOrRequire];
150
155
 
151
156
  // if a copyright comment is present we append it to the new program node
152
157
  if (copyright && visitor.parent) {
@@ -157,11 +162,36 @@ function wrap(visitor, programNode, opts) {
157
162
  function hasUseStrict(node) {
158
163
  return (node.directives || []).some(directive => directive.value.value === "use strict");
159
164
  }
160
- function generateDefine(body, imports, exportGlobal) {
165
+ function hasUseSapUiRequire(comments, body, remove) {
166
+ // detect the @sapUiRequire comment
167
+ return comments.some(comment => {
168
+ let found = false;
169
+ // check for existing comment block
170
+ if (comment.type === "CommentBlock") {
171
+ found = comment.value.trim() === "@sapUiRequire";
172
+ }
173
+ // remove the comment (if it is somewhere in the body)
174
+ if (found && remove) {
175
+ body === null || body === void 0 || body.forEach(node => {
176
+ (0, _core.traverse)(node, {
177
+ enter(path) {
178
+ ["leadingComments", "trailingComments", "innerComments"].forEach(key => {
179
+ var _path$node$key;
180
+ path.node[key] = (_path$node$key = path.node[key]) === null || _path$node$key === void 0 ? void 0 : _path$node$key.filter(c => c !== comment);
181
+ });
182
+ },
183
+ noScope: true
184
+ });
185
+ });
186
+ }
187
+ return found;
188
+ });
189
+ }
190
+ function generateDefineOrRequire(body, imports, exportGlobal, useRequire) {
161
191
  const defineOpts = {
162
192
  SOURCES: _core.types.arrayExpression(imports.map(i => _core.types.stringLiteral(i.src))),
163
193
  PARAMS: imports.map(i => _core.types.identifier(i.tmpName)),
164
194
  BODY: body
165
195
  };
166
- return exportGlobal ? th.buildDefineGlobal(defineOpts) : th.buildDefine(defineOpts);
196
+ return useRequire ? th.buildRequire(defineOpts) : exportGlobal ? th.buildDefineGlobal(defineOpts) : th.buildDefine(defineOpts);
167
197
  }
@@ -5,13 +5,18 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.buildInheritingFunction = exports.buildInheritingConstructor = exports.buildExtendAssign = exports.buildExportDefault = exports.buildDynamicImportHelper = exports.buildDefineGlobal = exports.buildDefine = exports.buildDefaultImportInterop = exports.buildDefaultImportDeconstructor = exports.buildDeclareExports = exports.buildConstDeclaration = exports.buildAssign = exports.buildAllExportHelper = exports.buildAllExport = void 0;
7
7
  exports.buildNamedExport = buildNamedExport;
8
- exports.exportsIdentifier = exports.buildTempExport = exports.buildReturnExports = exports.buildReturn = exports.buildNamedImportDestructor = void 0;
8
+ exports.exportsIdentifier = exports.buildTempExport = exports.buildReturnExports = exports.buildReturn = exports.buildRequire = exports.buildNamedImportDestructor = void 0;
9
9
  var _core = require("@babel/core");
10
10
  const exportName = "__exports";
11
11
  const exportsIdentifier = exports.exportsIdentifier = _core.types.identifier(exportName);
12
12
  const buildAssign = exports.buildAssign = (0, _core.template)(`
13
13
  OBJECT.NAME = VALUE;
14
14
  `);
15
+ const buildRequire = exports.buildRequire = (0, _core.template)(`
16
+ sap.ui.require(SOURCES, function (PARAMS) {
17
+ BODY;
18
+ });
19
+ `);
15
20
  const buildDefine = exports.buildDefine = (0, _core.template)(`
16
21
  sap.ui.define(SOURCES, function (PARAMS) {
17
22
  BODY;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "babel-plugin-transform-modules-ui5",
3
- "version": "7.4.2",
3
+ "version": "7.6.0",
4
4
  "description": "An unofficial babel plugin for SAP UI5.",
5
5
  "main": "dist/index.js",
6
6
  "repository": {
@@ -18,9 +18,9 @@
18
18
  "lint": "eslint src",
19
19
  "lint:fix": "npm run lint -- --fix",
20
20
  "lint:staged": "lint-staged",
21
- "test": "jest __test__ --coverage=no",
21
+ "test": "FORCE_COLOR=0 jest __test__ --coverage=no",
22
22
  "test:watch": "npm test -- --watch",
23
- "test:update-snapshot": "jest --updateSnapshot",
23
+ "test:update-snapshot": "FORCE_COLOR=0 jest --updateSnapshot",
24
24
  "prepare": "npm test && npm run build",
25
25
  "format": "prettier --write 'src/**/*.js'"
26
26
  },
@@ -94,5 +94,5 @@
94
94
  "prettier": "^3.1.0",
95
95
  "rimraf": "^5.0.5"
96
96
  },
97
- "gitHead": "840510e9de26aa1e17946cf0f3a2b6395089e7e1"
97
+ "gitHead": "8db5f32610df08996a49b97529c8a8f3fabcc59c"
98
98
  }