Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui5-tooling-transpile): integrate ts-interface-generator #787

Merged
merged 1 commit into from
Aug 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ pnpm-lock.yaml
/**/*.svg
/**/*.png
/**/*.md
/**/*.gen.d.ts
3 changes: 3 additions & 0 deletions packages/ui5-tooling-transpile/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ npm install ui5-tooling-transpile --save-dev
- transformTypeScript: `boolean` (old alias: transpileTypeScript)
if enabled, the tooling extension transforms TypeScript sources; the default value is derived from the existence of a `tsconfig.json` in the root folder of the project - if the file exists the configuration option is `true` otherwise `false`; setting this configuration option overrules the automatic determination

- generateTsInterfaces: `boolean|undefined` (*experimental feature*)
option requires a dependency to the `@ui5/ts-interface-generator` when either the value of the option is `true` or `undefined` and the project is a TypeScript-based project or the `transformTypeScript` option is set to `true` - can be forced to be inactive by setting the option to `false` (only relevant for the middleware)

- generateDts: `boolean`
if enabled, the tooling extension will generate type definitions (`.d.ts`) files; by default for projects of type `library` this option is considered as `true` and for other projects such as `application` this option is considered as `false` by default (is only relevant in case of transformTypeScript is `true`)

Expand Down
32 changes: 30 additions & 2 deletions packages/ui5-tooling-transpile/lib/middleware.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable jsdoc/check-param-names */
const path = require("path");
const parseurl = require("parseurl");

/**
Expand All @@ -17,7 +18,7 @@ const parseurl = require("parseurl");
* [MiddlewareUtil]{@link module:@ui5/server.middleware.MiddlewareUtil} instance
* @param {object} parameters.options Options
* @param {string} [parameters.options.configuration] Custom server middleware configuration if given in ui5.yaml
* @returns {function} Middleware function to use
* @returns {Function} Middleware function to use
*/
module.exports = async function ({ log, resources, options, middlewareUtil }) {
const {
Expand All @@ -26,7 +27,8 @@ module.exports = async function ({ log, resources, options, middlewareUtil }) {
normalizeLineFeeds,
determineResourceFSPath,
transformAsync,
shouldHandlePath
shouldHandlePath,
resolveNodeModule
} = require("./util")(log);

const cwd = middlewareUtil.getProject().getRootPath() || process.cwd();
Expand Down Expand Up @@ -86,6 +88,32 @@ module.exports = async function ({ log, resources, options, middlewareUtil }) {
);
}

// if the TypeScript interfaces should be created, launch the ts-interface-generator in watch mode
if (config.generateTsInterfaces) {
const generateTSInterfacesAPI = resolveNodeModule(
"@ui5/ts-interface-generator/dist/generateTSInterfacesAPI",
cwd
);
if (generateTSInterfacesAPI) {
const { main } = require(generateTSInterfacesAPI);
try {
config.debug && log.info(`Starting "@ui5/ts-interface-generator" in watch mode...`);
main({
watch: true,
logLevel: config.debug ? log.constructor.getLevel() : "error",
config: path.join(cwd, "tsconfig.json")
});
} catch (e) {
log.error(e);
}
} else {
config.debug &&
log.warn(
`Missing dependency "@ui5/ts-interface-generator"! TypeScript interfaces will not be generated until dependency has been added...`
);
}
}

return async (req, res, next) => {
const pathname = parseurl(req)?.pathname;
if (pathname.endsWith(".js") && shouldHandlePath(pathname, config.excludes, config.includes)) {
Expand Down
2 changes: 1 addition & 1 deletion packages/ui5-tooling-transpile/lib/task.js
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ module.exports = async function ({ log, workspace /*, dependencies*/, taskUtil,
config.debug && log.info(` + [.d.ts] index.d.ts`);
const pckgJsonFile = path.join(cwd, "package.json");
if (fs.existsSync(pckgJsonFile)) {
const pckgJson = require(pckgJsonFile);
const pckgJson = JSON.parse(fs.readFileSync(pckgJsonFile, { encoding: "utf8" }));
if (!pckgJson.types) {
log.warn(
` /!\\ package.json has no "types" property! Add it and point to "index.d.ts" in build destination!`
Expand Down
22 changes: 19 additions & 3 deletions packages/ui5-tooling-transpile/lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,8 @@ module.exports = function (log) {
const tscJsonPath = path.join(cwd, "tsconfig.json");
const isTypeScriptProject = fs.existsSync(tscJsonPath);

// read package.json and tsconfig.json to determine whether to transpile dependencies or not
if (isTypeScriptProject && !config.transpileDependencies) {
// read tsconfig.json to determine whether to transpile dependencies or not
if (isTypeScriptProject && !config.transpileDependencies && fs.existsSync(tscJsonPath)) {
const tscJson = JSONC.parse(fs.readFileSync(tscJsonPath, { encoding: "utf8" }));
const tsDeps = tscJson?.compilerOptions?.types?.filter((typePkgName) => {
try {
Expand All @@ -221,6 +221,21 @@ module.exports = function (log) {
// derive whether TypeScript should be transformed or not
const transformTypeScript = config.transformTypeScript ?? config.transpileTypeScript ?? isTypeScriptProject;

// load the pkgJson to determine the existence of the @ui5/ts-interface-generator
// to automatically set the config option generateTsInterfaces (if this is a ts project)
let generateTsInterfaces = config.generateTsInterfaces;
const pkgJsonPath = path.join(cwd, "package.json");
if (transformTypeScript && generateTsInterfaces === undefined && fs.existsSync(pkgJsonPath)) {
const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, { encoding: "utf8" }));
const deps = [
...Object.keys(pkgJson?.dependencies || {}),
...Object.keys(pkgJson?.devDependencies || {})
];
if (deps.indexOf("@ui5/ts-interface-generator") !== -1) {
generateTsInterfaces = true;
}
}

// derive the includes/excludes from the configuration
const includes = config.includes || config.includePatterns || [];
const defaultExcludes = [".png", ".jpeg", ".jpg"]; // still needed?
Expand All @@ -244,6 +259,7 @@ module.exports = function (log) {
excludes,
filePattern,
omitTSFromBuildResult: config.omitTSFromBuildResult,
generateTsInterfaces,
generateDts: config.generateDts,
transpileDependencies: config.transpileDependencies,
transformAtStartup: config.transformAtStartup,
Expand Down Expand Up @@ -448,7 +464,7 @@ module.exports = function (log) {
* @param {string} pathname the path name
* @param {Array<string>} excludes exclude paths
* @param {Array<string>} includes include paths
* @returns true, if the path should be handled
* @returns {boolean} true, if the path should be handled
*/
shouldHandlePath: function shouldHandlePath(pathname, excludes = [], includes = []) {
return (
Expand Down
8 changes: 8 additions & 0 deletions packages/ui5-tooling-transpile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@
"devDependencies": {
"ava": "^5.3.1"
},
"peerDependencies": {
"@ui5/ts-interface-generator": ">=0.8.0"
},
"peerDependenciesMeta": {
"@ui5/ts-interface-generator": {
"optional": true
}
},
"scripts": {
"lint": "eslint lib",
"test": "ava --no-worker-threads"
Expand Down
35 changes: 20 additions & 15 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion showcases/ui5-tsapp-simple/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@
"@types/openui5": "1.117.0",
"@typescript-eslint/eslint-plugin": "^6.3.0",
"@typescript-eslint/parser": "^6.3.0",
"@ui5/ts-interface-generator": "^0.8.0",
"@ui5/cli": "^3.4.0",
"eslint": "^8.47.0",
"eslint": "^8.46.0",
"rimraf": "^5.0.1",
"typescript": "^5.1.6",
"ui5-middleware-livereload": "workspace:^",
Expand Down
19 changes: 19 additions & 0 deletions showcases/ui5-tsapp-simple/webapp/control/SimpleControl.gen.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { PropertyBindingInfo } from "sap/ui/base/ManagedObject";
import { $ControlSettings } from "sap/ui/core/Control";

declare module "./SimpleControl" {

/**
* Interface defining the settings object used in constructor calls
*/
interface $SimpleControlSettings extends $ControlSettings {
text?: string | PropertyBindingInfo;
}

export default interface SimpleControl {

// property: text
getText(): string;
setText(text: string): this;
}
}
36 changes: 36 additions & 0 deletions showcases/ui5-tsapp-simple/webapp/control/SimpleControl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Control from "sap/ui/core/Control";
import RenderManager from "sap/ui/core/RenderManager";
import type { MetadataOptions } from "sap/ui/core/Element";

/**
* @namespace ui5.ecosystem.demo.simpletsapp.control
*/
export default class SimpleControl extends Control {
static readonly metadata: MetadataOptions = {
properties: {
text: "string",
},
};

// The following three lines were generated and should remain as-is to make TypeScript aware of the constructor signatures
constructor(idOrSettings?: string | $SimpleControlSettings);
constructor(id?: string, settings?: $SimpleControlSettings);
constructor(id?: string, settings?: $SimpleControlSettings) {
super(id, settings);
}

renderer = {
apiVersion: 2,
render: (rm: RenderManager, control: SimpleControl) => {
rm.openStart("div", control);
rm.style("font-size", "2rem");
rm.style("width", "2rem");
rm.style("height", "2rem");
rm.style("display", "inline-block");
rm.style("color", "blue");
rm.openEnd();
rm.text(control.getText());
rm.close("div");
},
};
}
2 changes: 1 addition & 1 deletion showcases/ui5-tslib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"@typescript-eslint/eslint-plugin": "^6.3.0",
"@typescript-eslint/parser": "^6.3.0",
"@ui5/cli": "^3.4.0",
"@ui5/ts-interface-generator": "^0.7.0",
"@ui5/ts-interface-generator": "^0.8.0",
"eslint": "^8.47.0",
"karma": "^6.4.2",
"karma-chrome-launcher": "^3.2.0",
Expand Down
Loading