Skip to content

Commit

Permalink
Merge pull request #19 from r-murphy/fix/move-module-transform-out-of…
Browse files Browse the repository at this point in the history
…-program-enter-visitor

Move module transform out of program enter visitor and into program exit
  • Loading branch information
r-murphy committed Feb 13, 2019
2 parents 08f7175 + 46ad4b5 commit f2f67b9
Show file tree
Hide file tree
Showing 22 changed files with 663 additions and 568 deletions.
2 changes: 1 addition & 1 deletion LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2017 Ryan Murphy
Copyright (c) 2019 Ryan Murphy

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
73 changes: 45 additions & 28 deletions README.md
@@ -1,16 +1,14 @@
# babel-ui5
# babel transform-ui5

An unofficial Babel transformer plugin for SAP/Open UI5.

It allows you to develop SAP UI5 applications by using the latest [ECMAScript](http://babeljs.io/docs/learn-es2015/), including classes and modules, or even TypeScript.

**WARNING Currently not compatible with @babel/preset-typescript**

[![Build Status](https://travis-ci.org/r-murphy/babel-plugin-transform-modules-ui5.svg?branch=master)](https://travis-ci.org/r-murphy/babel-plugin-transform-modules-ui5)

## Install

This repo contains both a preset and a plugin. It is recommended to use the preset.
This repo contains both a preset and a plugin. It is recommended to use the preset since the plugin will get split into two in the future.

```sh
npm install babel-preset-transform-ui5 --save-dev
Expand All @@ -26,7 +24,7 @@ yarn add babel-preset-transform-ui5 --dev

### .babelrc

At a minimum, add `transform-modules-ui5` to the `plugins`.
At a minimum, add `transform-ui5` to the `presets`.

```js
{
Expand All @@ -46,28 +44,46 @@ Or if you want to supply plugin options, use the array syntax.
}
```

At the time of writing, the babel version is 7.0, which does not natively support class property syntax. To use that syntax also add the plugin `@babel/plugin-syntax-class-properties`.
At the time of writing the babel version is 7.3, which does not natively support class property syntax. To use that syntax also add the plugin `@babel/plugin-syntax-class-properties`.

It is also recommended to use [@babel/preset-env](https://babeljs.io/docs/en/next/babel-preset-env.html) to control which ES version the final code is transformed to.

The order of presets is important and `@babel/preset-env` should be higher in the array than this one. Babel applies them in reverse order for legacy reasons, so this preset will be applied first.

```js
{
"presets": [
["@babel/preset-env", { // applied 3rd
...presetEnvOpts
}],
["transform-ui5", { // applied 2nd
...pluginOpts
}],
"@babel/preset-typescript", // applied 1st
]
}
```

## Features

There are 2 main feature categories of the plugin, and you can use both or one without the other.:
There are 2 main features of the plugin, and you can use both or one without the other:

1. Converting ES modules (import/export) into sap.ui.define or sap.ui.require.
2. Converting ES classes into Control.extend(..) syntax.

This only transforms the UI5 relevant things. It does not transform everything to ES5 (for example it does not transform const/let to var). This makes it easier to use `babel-preset-env` to determine how to transform everything else.
**NOTE:** The class transform will be split into its own plugin in the future.

This only transforms the UI5 relevant things. It does not transform everything to ES5 (for example it does not transform const/let to var). This makes it easier to use `@babel/preset-env` to transform things correctly.

A more detailed list includes:
A more detailed feature list includes:

- ES2015 Imports (default, named, and dynamic)
- ES2015 Exports (default and named)
- Class, using inheritance and `super` keyword
- Classes, using inheritance and `super` keyword
- Static methods and fields
- Class properties
- Class property arrow functions are bound correctly in the constructor.
- Existing `sap.ui.define` calls don't get wrapped but can still be converted.
- Existing `sap.ui.define` calls don't get wrapped but classes within can still be converted.
- Fixes `constructor` shorthand method, if used.
- Various options to control the class name string used.
- JSDoc (name, namespace, alias)
Expand All @@ -76,7 +92,7 @@ A more detailed list includes:

### Converting ES modules (import/export) into sap.ui.define or sap.ui.require

The plugin will wrap any code having import/export statements in an sap.ui.define. If there is no import/export, it won't wrap.
The plugin will wrap any code having import/export statements in an sap.ui.define. If there is no import/export, it won't be wrapped.

#### Static Import

Expand All @@ -85,13 +101,13 @@ The plugin supports all of the ES import statements, and will convert them into
```js
import Default from "module";
import Default, { Named } from "module";
import { Named, Named2 } from "module";
import * as Name from "module";
import { Named1, Named2 } from "module";
import * as Namespace from "module";
```

The plugin uses a temporary name (as needed) for the initial imported variable, and then extracts the properties from it as needed.
This allows importing ES Modules which have a 'default' value, and also non-ES modules which don't.
The plugin also allows for merged imports statements from the same source path into a single require and then deconstructs it accordingly.
The plugin uses a temporary name (as needed) for the initial imported variable, and then extracts the properties from it.
This allows importing ES Modules which have a 'default' value, and also non-ES modules which do not.
The plugin also merges imports statements from the same source path into a single require and then deconstructs it accordingly.

This:

Expand Down Expand Up @@ -144,7 +160,8 @@ export { X as default };
export let v; v = 'v'; // NOTE that the value here is currently not a live binding (http://2ality.com/2015/07/es6-module-exports.html)
```
Export is a bit trickier if you want your exported module to be imported by code that does not include the import inter-op. If the importing code has the inter-op logic inserted by this plugin, then you don't need to worry, and can disable the export inter-op features if desired.
Export is a bit trickier if you want your exported module to be imported by code that does not include an import inter-op.
If the importing code has an inter-op logic inserted by this plugin, then you don't need to worry and can disable the export inter-op features if desired.
Imagine a file like this:
Expand All @@ -159,7 +176,7 @@ export default {
};
```
Which might create an exported module that looks like:
Which might export a module that looks like:
```js
{
Expand Down Expand Up @@ -205,7 +222,7 @@ In order to determine which properties the default export already has, the plugi
export default {
prop: val,
};
// plugin knows about prop
// plugin knows about 'prop'
```
- In a variable declaration literal or assigned afterwards.
Expand All @@ -216,10 +233,10 @@ const Module = {
};
Module.prop2 = val2;
export default Module;
// plugin knows about prop1 and prop2
// plugin knows about 'prop1' and 'prop2'
```
- In an Object.assign(..) or \_extends(..)
- In an `Object.assign(..)` or `_extends(..)`
- \_extends is the named used by babel and typescript when compiling object spread.
- This includes a recursive search for any additional objects used in the assign/extend which are defined in the upper block scope.
Expand All @@ -231,10 +248,10 @@ const object2 = Object.assign({}, object1, {
prop2: val,
});
export default object2;
// plugin knows about prop1 and prop2
// plugin knows about 'prop1' and 'prop2'
```
**CAUTION**: The plugin cannot check the properties on imported modules. So if they are used in Object.assign() or \_extends(), the plugin will not be aware of its properties and may override them with a named export.
**CAUTION**: The plugin cannot check the properties on imported modules. So if they are used in `Object.assign()` or `_extends()`, the plugin will not be aware of its properties and may override them with a named export.
##### Example non-solvable issues
Expand All @@ -254,7 +271,7 @@ function one_string() {
}

const MyUtil = {
// The plugin can't assign these to `exports` since the definition is not just a reference to the named export.
// The plugin can't assign 'one' or 'two' to `exports` since there is a collision with a different definition.
one: one_string,
two: () => "two",
};
Expand Down Expand Up @@ -320,8 +337,8 @@ sap.ui.define(["./a"], A => {
### Converting ES classes into Control.extend(..) syntax
By default, the plugin converts ES classes to Control.extend(..) syntax if the class extends from a class which has been imported.
So a class without a parent will not be extended.
By default, the plugin converts ES classes to `Control.extend(..)` syntax if the class extends from a class which has been imported.
So a class without a parent will not be converted to .extend() syntax.
There are a few options or some metadata you can use to control this.
Expand Down Expand Up @@ -672,4 +689,4 @@ Issues also welcome for feature requests.
## License
MIT © 2017 Ryan Murphy
MIT © 2019 Ryan Murphy
17 changes: 17 additions & 0 deletions packages/plugin-transform-classes/README.md
@@ -0,0 +1,17 @@
# babel-plugin-transform-classes-ui5

[Docs](../../README.md)

There is also a preset which should be used rather than using the plugin directly.

## Install

```sh
npm install babel-plugin-transform-classes-ui5 --save-dev
```

or

```sh
yarn add babel-plugin-transform-classes-ui5 --dev
```
19 changes: 19 additions & 0 deletions packages/plugin-transform-classes/package.json
@@ -0,0 +1,19 @@
{
"name": "babel-plugin-transform-classes-ui5",
"version": "7.0.0-rc.5",
"description": "An unofficial babel plugin for SAP UI5.",
"main": "src/index.js",
"repository": "https://github.com/r-murphy/babel-plugin-ui5/tree/master/packages/plugin-transform-classes",
"engines": {
"node": ">=6"
},
"scripts": {},
"author": "Ryan Murphy",
"license": "MIT",
"bugs": {
"url": "https://github.com/r-murphy/babel-plugin-transform-modules-ui5/issues"
},
"homepage": "https://github.com/r-murphy/babel-plugin-transform-modules-ui5#readme",
"dependencies": {},
"devDependencies": {}
}
3 changes: 3 additions & 0 deletions packages/plugin-transform-classes/src/index.js
@@ -0,0 +1,3 @@
return {
visitor: {},
};
24 changes: 19 additions & 5 deletions packages/plugin/__test__/__snapshots__/test.js.snap
Expand Up @@ -1173,10 +1173,8 @@ exports[`no-wrap skip-iffe.js 1`] = `
})();"
`;
exports[`presets with-preset-env.js 1`] = `
"\\"use strict\\";
sap.ui.define([\\"sap/class\\"], function (SAPClass) {
exports[`preset-env preset-env-class.js 1`] = `
"sap.ui.define([\\"sap/class\\"], function (SAPClass) {
var MyClass = SAPClass.extend(\\"my.MyClass\\", {
x: 1,
fn: function _fn() {
Expand All @@ -1192,6 +1190,22 @@ sap.ui.define([\\"sap/class\\"], function (SAPClass) {
});"
`;
exports[`preset-env preset-env-usage.js 1`] = `
"sap.ui.define([\\"sap/SAPClass\\", \\"core-js/modules/es6.promise\\", \\"core-js/modules/es6.string.includes\\", \\"core-js/modules/es7.array.includes\\"], function (SAPClass, __core_js_modules_es6promise, __core_js_modules_es6stringincludes, __core_js_modules_es7arrayincludes) {
var AClass = SAPClass.extend(\\"my.MyClass\\", {
delay: function _delay() {
return new Promise(function (resolve) {
setTimeout(resolve);
});
},
includes: function _includes(str) {
return str.includes(\\"thing\\");
}
});
return AClass;
});"
`;
exports[`typescript ts-class-props.ts 1`] = `
"sap.ui.define([\\"sap/Class\\"], function (SAPClass) {
const MyClass = SAPClass.extend(\\"MyClass\\", {
Expand All @@ -1208,7 +1222,7 @@ exports[`typescript ts-class-props.ts 1`] = `
exports[`typescript ts-export-interface.ts 1`] = `
"sap.ui.define([], function () {
const MY_CONSTANT = \\"constant\\";
let MyEnum;
var MyEnum;
(function (MyEnum) {
MyEnum[MyEnum[\\"A\\"] = 0] = \\"A\\";
Expand Down
15 changes: 15 additions & 0 deletions packages/plugin/__test__/fixtures/preset-env/preset-env-usage.js
@@ -0,0 +1,15 @@
import SAPClass from "sap/SAPClass";

/**
* @name my.MyClass
*/
export default class AClass extends SAPClass {
delay() {
return new Promise(resolve => {
setTimeout(resolve);
});
}
includes(str) {
return str.includes("thing");
}
}
4 changes: 3 additions & 1 deletion packages/plugin/__test__/test.js
Expand Up @@ -41,7 +41,9 @@ function processDirectory(dir) {
presets.push([
"@babel/preset-env",
{
// default targets for preset-env is ES5
targets: undefined, // default targets for preset-env is ES5
modules: false,
useBuiltIns: "usage",
},
]);
}
Expand Down
5 changes: 3 additions & 2 deletions packages/plugin/package.json
Expand Up @@ -10,6 +10,7 @@
"scripts": {
"clean": "rimraf dist",
"build": "babel src -d dist",
"build:watch": "babel src -d dist --watch",
"lint": "eslint src",
"lint:fix": "npm run lint -- --fix",
"lint:staged": "lint-staged",
Expand Down Expand Up @@ -38,9 +39,9 @@
"author": "Ryan Murphy",
"license": "MIT",
"bugs": {
"url": "https://github.com/r-murphy/babel-plugin-ui5/issues"
"url": "https://github.com/r-murphy/babel-plugin-transform-modules-ui5/issues"
},
"homepage": "https://github.com/r-murphy/babel-plugin-ui5#readme",
"homepage": "https://github.com/r-murphy/babel-plugin-transform-modules-ui5#readme",
"jest": {
"collectCoverage": true
},
Expand Down
@@ -1,9 +1,11 @@
import { types as t } from "@babel/core";
import * as th from "./templates";

import Path from "path";
import assignDefined from "object-assign-defined";

import * as ast from "./ast";
import * as th from "../../utils/templates";
import * as ast from "../../utils/ast";

import { getJsDocClassInfo, getTags } from "./jsdoc";
import { getDecoratorClassInfo } from "./decorators";

Expand Down
File renamed without changes.
@@ -1,10 +1,8 @@
import { types as t } from "@babel/core";
import doctrine from "doctrine";
import ignoreCase from "ignore-case";
import { isCommentBlock } from "./ast";

const classInfoValueTags = ["alias", "name", "namespace"];

const classInfoBoolTags = ["nonUI5", "controller", "keepConstructor"];

export function getJsDocClassInfo(node, parent) {
Expand Down Expand Up @@ -33,8 +31,12 @@ export function getJsDocClassInfo(node, parent) {
})
.filter(notEmpty)[0];
}
// Else see if the JSDoc are on the return statement (i..e return class X extends SAPClass)
else if (t.isClassExpression(node) && t.isReturnStatement(parent)) {
// Else see if the JSDoc are on the return statement (eg. return class X extends SAPClass)
// or export statement (eg. export default class X extends SAPClass)
else if (
(t.isClassExpression(node) && t.isReturnStatement(parent)) ||
(t.isClassDeclaration(node) && t.isExportDefaultDeclaration(parent))
) {
return getJsDocClassInfo(parent);
} else {
return {};
Expand Down Expand Up @@ -98,3 +100,8 @@ export function hasJsdocGlobalExportFlag(node) {
);
});
}

// This doesn't exist on babel-types
function isCommentBlock(node) {
return node && node.type === "CommentBlock";
}

0 comments on commit f2f67b9

Please sign in to comment.