さて、JavaScriptでコードを記述しようと思っても、Node.jsのnpmで配布されているようなパッケージが無い、となると相当に大変な作業になってしまいます。
Oracle Database 23aiのMLEモジュールは、基本的にESモジュールです。npmのパッケージをESモジュールとして取得すれば、(原則的に)それをもとにMLEモジュールを作成できます。
CDNのjsDelivrでは、NPMパッケージを取得するURLの末尾に+esmを付加することにより、そのパッケージをESモジュールの形式で返してくれます。
今回はJavaScriptを清書するパッケージpretty-jsをデータベースにロードしてみます。
pretty-jsをESモジュールとして取得するURLは以下になります。
CDNからESモジュールを取得し、MLEモジュールとして作成するAPEXアプリケーションを作成しています。相当にやっつけ仕事ですが、何もないよりは遥かに良いです。エクスポートは以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/mle-module-manager.zip
以下のGIF動画のように操作します。
- Module Nameにpretty-jsを指定し、Add Moduleをクリックします。jsDelivrからpretty-jsをESモジュールとして取得し、MLEモジュールの作成対象として追加します。
- Resolve Onceをクリックします。pretty-jsのESモジュールのコードを読んで、importされているESモジュールを、MLEモジュールの作成対象として追加します。
- 追加されたESモジュールを再帰的に解析するようにはしていないので、ESモジュールがすべて解析されるまで、Resolve Onceを複数回クリックします。
- Create Mle Modulesをクリックし、jsDelivrから取得したESモジュールを元にMLEモジュールを作成します。
- MLE Envに指定されているMLE環境(なければ新規作成)に、作成したMLEモジュールのインポートを追加します。
動作確認のために、以下のコードを実行しています。
const { default:prettyJs } = await import("/npm/pretty-js@0.2.2/+esm");
const { fetch } = await import("mle-js-fetch");
fetch('https://cdn.jsdelivr.net/npm/pretty-js@0.2.2/+esm')
.then(response => {
return response.text();
})
.then(text => {
console.log(prettyJs(text));
});
ほとんどの処理は、記事の末尾に添付したパッケージUTL_MLE_NPMに実装しています。APEXアプリケーションのボタンをクリックすると、対応したUTL_MLE_NPMのプロシージャを呼び出します(プロセスのタイプがAPI呼出し)。
- ボタンINITだけはUTL_MLE_NPMではなく、APEX_COLLECTION.CREATE_OR_TRUNCATE_COLLECTIONを呼び出し、APEXコレクションMLE_MODULESを初期化します。
- ボタンADD_ES_MODULEは、UTL_MLE_NPM.ADD_MODULEを呼び出します。
- ボタンRESOLVE_ONCEは、UTL_MLE_NPM.RESOLVE_ONCEを呼び出します。
- ボタンCREATE_MLE_MODULESは、UTL_MLE_NPM.CREATE_MLE_MODULESを呼び出します。
- ボタンADD_IMPORTSは、UTL_MLE_NPM.ADD_IMPORTSを呼び出します。
- ボタンDROP_MLE_ENVは、drop mle env MLE環境名を実行します。
- ボタンDROP_MLE_MODULESは、UTL_MLE_NPM.DROP_MLE_MODULES_ESMを呼び出します。
ボタンに対応したプロセスが作成されています。
上記の例ではpretty-jsをMLEモジュールとして作成しましたが、元々はjimpをロードするために、このアプリケーションを作成しました。
Module Nameにjimpを入力し、Add Es Moduleをクリックします。ESモジュールは以下のURLから取得しています。
https://cdn.jsdelivr.net/npm/jimp/+esm
バージョンを指定していないため、ESモジュールの内容からバージョンを取り出しています。
レポートの列C001はモジュール名、C002はバージョンです。列C007はMLEモジュール名です。ESモジュールの名前は記号(例えば@)で始まることもあるため、MLEモジュール名は必ずESM_で開始するようにしています。また、MLEモジュールにはバージョン情報を付けることができますが、同じモジュール名でバージョンが異なるモジュールは作成できないようです。同じモジュールでもバージョンが異なるものを別のMLEモジュールとして作成できるように、MLEモジュール名にはバージョンを付加しています。
列N001は0の場合は、MLEモジュールとして未作成、1の場合は作成済み、列N002は0の場合はソースコードは未解析、1の場合は解析済み(インポートしているESモジュールがAPEXコレクションに追加済み)です。
ボタンResolve Onceをクリックします。
列C001のjimpの列N002が1になり(つまり解析済み)、@jimp/custom、@jimp/types、@jimp/pluginsが行として追加されます。これらの列N002は0で、まだ解析されていません。
再度Resolve Onceをクリックします。@jimp/custom、@jimp/types、@jimp/pluginsの内容が解析され(列N002は1になります)、インポートされていたESモジュールがAPEXコレクションに追加されます。
同じ操作(Resolve Onceのクリック)を、列N002の値がすべて1(ESモジュールの取得に失敗しているものを除く)になるまで繰り返します。
列C002(バージョン)が空白のESモジュールが2つあります。@jimp/coreとgifwrapです。
バージョンが取れないのは、jsDelivrの側でESモジュールのコード生成に失敗しているためです。生成されたコードを確認します。
https://cdn.jsdelivr.net/npm/@jimp/core/+esm
https://cdn.jsdelivr.net/npm/gifwrap/+esm
両方ともに以下のような出力になっています。どのようなNPMでも、ESモジュールにできるわけではないようです。
/**
* Failed to bundle using Rollup v2.79.1: the file imports a not supported node.js built-in module "fs".
* If you believe this to be an issue with jsDelivr, and not with the package itself, please open an issue at https://github.com/jsdelivr/jsdelivr
*/
throw new Error('Failed to bundle using Rollup v2.79.1: the file imports a not supported node.js built-in module "fs". If you believe this to be an issue with jsDelivr, and not with the package itself, please open an issue at https://github.com/jsdelivr/jsdelivr');
また、MLEモジュールとして作成できていても、インポートして使おうとするとエラーが発生する場合もあります。MLE環境に追加されたインポート名より、MLEモジュールをインポートする文を生成します。MLE環境はMYENVとしてます。
select '// const e' || rownum || ' = await import("' || import_name || '");' from user_mle_env_imports where env_name = 'MYENV'
以下のインポートを実行してみます。
const e5 = await import("/npm/@jimp/custom@0.22.12/+esm");
以下のエラーが発生します。モジュール@jimp/coreはESモジュールの生成に失敗しているため、MLEモジュールとして作成できていません。@jimp/coreのコードを修正する以外に対応方法は無いと思われます。
ORA-04161: Error: Cannot load ES module: /npm/@jimp/core@0.22.12/+esm
以下のインポートを実行してみます。
const e49 = await import("/npm/xml-parse-from-string@1.0.1/+esm");
以下のエラーが発生しました。
ORA-04161: ReferenceError: self is not defined
ORA-06512: "APEX_230200.WWV_FLOW_CODE_EXEC_MLE", 行728
ORA-04171: 場所:module:eval (WKSP_APEXDEV.ESM_XML_PARSE_FROM_STRING_1_0_1:7:16)
ESモジュールのコードを確認してみます。
https://cdn.jsdelivr.net/npm/xml-parse-from-string@1.0.1/+esm
コード中でMicrosoft.XMLDOMを参照している模様です。Oracle Database上ではエラーが発生するのは仕方がなさそうです。
/**
* Bundled by jsDelivr using Rollup v2.79.1 and Terser v5.19.2.
* Original file: /npm/xml-parse-from-string@1.0.1/index.js
*
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
*/
var e=void 0!==self.DOMParser?function(e){return(new self.DOMParser).parseFromString(e,"application/xml")}:void 0!==self.ActiveXObject&&new self.ActiveXObject("Microsoft.XMLDOM")?function(e){var r=new self.ActiveXObject("Microsoft.XMLDOM");return r.async="false",r.loadXML(e),r}:function(e){var r=document.createElement("div");return r.innerHTML=e,r};export{e as default};
//# sourceMappingURL=/sm/be5cb35a93829eb0b811add8f6983796069c4d7086c8acebcc12b0d69a3be01d.map
最終的にjimpはロードできなかったのは残念ですが、依存関係のあるNPMモジュールからMLEモジュールを作成する作業は、それなりに効率的にできるようになりました。
今回の記事は以上になります。
Oracle APEXのアプリケーション作成の参考になれば幸いです。
完
UTL_MLE_NPMパッケージ
jsdelivr向け実装