Skip to content

Commit

Permalink
fix: support index modules for dependencies (#123)
Browse files Browse the repository at this point in the history
CommonJS or ES modules require index modules when a folder is
required, e.g.:

```js
import * from "./schema"
```

will in case of `./schema` is a directory lookup the index module
and require the following:

```js
import * from "./schema/index"
```

under the hood. As the UI5 module loader cannot support this feature
this Babel plugin checks for the existence of the index module and
rewrites the import to the index module so that the UI5 loader also
loads the proper module.
  • Loading branch information
petermuessig authored Apr 10, 2024
1 parent cf41a67 commit f510380
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 9 deletions.
52 changes: 50 additions & 2 deletions packages/plugin/__test__/__snapshots__/test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1492,7 +1492,7 @@ exports[`mixed mixed-import-export.js 1`] = `
__esModule: true
};
function extendExports(exports, obj) {
Object.keys(obj).forEach(function (key) {
obj && Object.keys(obj).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
Expand Down Expand Up @@ -1875,6 +1875,31 @@ exports[`typescript ts-export-type.ts 1`] = `
});"
`;
exports[`typescript ts-export-type-only.ts 1`] = `""`;
exports[`typescript ts-index.ts 1`] = `
"sap.ui.define(["./_private_/index"], function (____private__index) {
"use strict";
var __exports = {
__esModule: true
};
function extendExports(exports, obj) {
obj && Object.keys(obj).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function get() {
return obj[key];
}
});
});
}
extendExports(__exports, ____private__index);
return __exports;
});"
`;
exports[`typescript ts-re-export.ts 1`] = `
"sap.ui.define(["module", "./having-a-dash", "path/having-a-dash"], function (__module, ___having_a_dash, __path_having_a_dash) {
"use strict";
Expand All @@ -1883,7 +1908,7 @@ exports[`typescript ts-re-export.ts 1`] = `
__esModule: true
};
function extendExports(exports, obj) {
Object.keys(obj).forEach(function (key) {
obj && Object.keys(obj).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
Expand All @@ -1900,6 +1925,29 @@ exports[`typescript ts-re-export.ts 1`] = `
});"
`;
exports[`typescript ts-re-export-type-only.ts 1`] = `
"sap.ui.define(["./ts-export-type-only"], function (___ts_export_type_only) {
"use strict";
var __exports = {
__esModule: true
};
function extendExports(exports, obj) {
obj && Object.keys(obj).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function get() {
return obj[key];
}
});
});
}
extendExports(__exports, ___ts_export_type_only);
return __exports;
});"
`;
exports[`typescript-preset-env ts-class-anonymous.ts 1`] = `
"sap.ui.define(["sap/Class"], function (SAPClass) {
"use strict";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./type";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type StringOrBoolean = string | boolean;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* any sequence of octets
*/
export type Binary = any;
1 change: 1 addition & 0 deletions packages/plugin/__test__/fixtures/typescript/ts-index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./_private_";
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./ts-export-type-only";
33 changes: 27 additions & 6 deletions packages/plugin/src/modules/visitor.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
import { join, dirname } from "path";
import { existsSync, statSync } from "fs";

import { types as t, template } from "@babel/core";
import * as th from "../utils/templates";
import * as ast from "../utils/ast";

import { hasJsdocGlobalExportFlag } from "../classes/helpers/jsdoc";

const resolveSource = (src, filename) => {
src = src.replace(/\\/g, "/");
const dir = dirname(filename);
const absoluteSrc = join(dir, src);
if (existsSync(absoluteSrc) && statSync(absoluteSrc).isDirectory) {
if (
existsSync(join(absoluteSrc, "index.js")) ||
existsSync(join(absoluteSrc, "index.jsx")) ||
existsSync(join(absoluteSrc, "index.ts")) ||
existsSync(join(absoluteSrc, "index.tsx")) ||
existsSync(join(absoluteSrc, "index.mjs")) ||
existsSync(join(absoluteSrc, "index.cjs"))
) {
src = `${src}/index`;
}
}
return src;
};
const cleanImportSource = (src) =>
src.replace(/(\/)|(-)|(@)/g, "_").replace(/\./g, "");
const tempModuleName = (name) => `__${name}`;
Expand All @@ -23,13 +44,13 @@ export const ModuleTransformVisitor = {
/*!
* Removes the ES6 import and adds the details to the import array in our state.
*/
ImportDeclaration(path, { opts = {}, ...state }) {
ImportDeclaration(path, { filename, opts = {}, ...state }) {
const { node } = path;

if (node.importKind === "type") return; // flow-type

const { specifiers, source } = node;
const src = source.value.replace(/\\/g, "/");
const src = resolveSource(source.value, filename);

// When 'libs' are used, only 'libs' will be converted to UI5 imports.
const { libs = [".*"] } = opts;
Expand Down Expand Up @@ -168,14 +189,14 @@ export const ModuleTransformVisitor = {
* The reason we don't export in place is to handle the situation
* where a let or var can be defined, and the latest one should be exported.
*/
ExportNamedDeclaration(path) {
ExportNamedDeclaration(path, { filename }) {
const { node } = path;
const { specifiers, declaration, source } = node;

let fromSource = "";
if (source) {
// e.g. export { one, two } from 'x'
const src = source.value;
const src = resolveSource(source.value, filename);
const name = cleanImportSource(src);
const tmpName = tempModuleName(name);
this.imports.push({ src, name, tmpName });
Expand Down Expand Up @@ -267,8 +288,8 @@ export const ModuleTransformVisitor = {
}
},

ExportAllDeclaration(path) {
const src = path.node.source.value;
ExportAllDeclaration(path, { filename }) {
const src = resolveSource(path.node.source.value, filename);
const name = cleanImportSource(src);
const tmpName = tempModuleName(name);

Expand Down
2 changes: 1 addition & 1 deletion packages/plugin/src/utils/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function buildNamedExport(obj) {

export const buildAllExportHelper = template(`
function extendExports(exports, obj) {
Object.keys(obj).forEach(function (key) {
obj && Object.keys(obj).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
Expand Down

0 comments on commit f510380

Please sign in to comment.