JHIPSTER一覧

目次

はじめに

jhipsterでなにが、うれしいかというと、 クラス図をJDLという簡易な設定さえ書けば、

一応動作する画面が、できるということだと思う。

リレーションとかも、考慮済みのテンプレートが、いい感じに、頑張ってくれる。

でも、すでに、自分のプロジェクトのひな型ができている場合がある。

成果物として、望まれているのは、自分のプロジェクトの形式に沿ったコード

だったりする。

そうなるとひな形のほうを修正したい。

もし、ページを追加したい場合

例として、おみくじページの追加例がある。モジュールと呼んでいるらしい。

https://www.jhipster.tech/modules/creating-a-module/

モジュール作成のジェネレータもある

https://github.com/jhipster/generator-jhipster-module

ためしに使ってみる

自分がデザインとかしたページのテンプレがつくれたらいいな。

インストールする

npm install -g generator-jhipster-module
結論から言うと、これ、windowsだと、うまくいかなかった。yoemanのgenerator-generatorは、動作したから、このモジュールの、jhipsterの読み込みが、新しいJHIPSTERに対応してないっぽい感じ

自分用のモジュールのディレクトリ作る

mkdir generator-jhipster-mymod
cd generator-jhipster-mymod

git使うので、gitのリポジトリとするための初期化

git init

yoコマンドで、作るための質問に答える。(Windowsの場合、実行は、PowerShell?)

yo jhipster-module

質問はこんな感じでした。

? What is the base name of your module? (helloworld)

そのまま、エンター、モジュール名だろうか。

? Give a description of your module

モジュールの説明だろうか

description-hello

と、回答

? Do you want to enable hooks for your module from JHipster generator? (Use arrow keys)
> No, This is a stand alone module
  Yes, Enable post entity hook

とりあえずNoが、えらばれていたので、そのままEnter

? What is your GitHub username?

GitHub?使うこと前提?とりあえず、入力

? Who are you? Firstname Lastname (Firstname Lastname)

氏名を入力

? Your email?

メアドを入力

? Your home page url?

ホームページを入力

> No license
  Apache License 2.0
  GNU General Public License v3.0
  MIT License

そのまま、NoLicense?にした

結果

   create package.json
   create .editorconfig
   create .eslintignore
   create .eslintrc.json
   create .gitattributes
   create .gitignore
   create .travis.yml
   create README.md
   create test\test-app.js
   create generators\app\index.js
   create generators\app\templates\dummy.txt

##### USAGE #####
To begin to work:
- launch: npm install or yarn install
- link: npm link or yarn link
- test your module in a JHipster project:
    - go into your JHipster project
    - link to your module: npm link generator-jhipster-helloworld or yarn link 
generator-jhipster-helloworld
    - launch your module: yo jhipster-helloworld
- then, come back here, and begin to code!

この、書き込んだ値は、package.jsonというファイルに、出力されている。

そこに対応しているJHIPSTERのバージョンが書いてある。

今使っているのは、JHIPSTER6なんだけど、このツールが対応しているのは、JHIPSTER5

みたい、うごかないかもしれない。

実際に、やってみると、

You don't seem to have a generator with the name “jhipster:modules” installed.
But help is on the way:

と、エラーがでた。

自分の直感としては、

npm linkがwindowsでうまくうごいてないのかな?と思ってみたり

して、しらべてみると、

このjhipsterのモジュールジェネレータの作りは、

yoemanのgenerator-generatorをベースにつくっているのだとおもわれます。

質問が、結構似ていたから。多分そうです。

これで、自分のJHIPSTERで作成したプロジェクトに、ディレクトリを移動して、

npm link generator-jhipster-helloworld

yo jhipster-helloworld

で、確認できるみたい。

シンボリックリンクが鬼門

下記のサイトを見ると、

npm link

をつかうと、globalフォルダにgeneratorが作成されてしまうので、パーミッションの問題や同名のファイルを作成できない等の問題があります。

https://yosuke-furukawa.hatenablog.com/entry/2013/07/14/131903

なので、こんな感じでやっていらっしゃる方がいて、

$ mkdir helloworld
$ cd helloworld
$ mkdir node_modules
$ ln -s <generator-helloworldのパス> node_modules/.

さらに、毎回これやるのが面倒だということで、下記のツールをつくったとありました。

https://github.com/yosuke-furukawa/yeomania

npm linkの順番も関係あるの?

また、npm linkの順番も、下記のサイトをみると、関係あるとかいてありました。

https://60devs.com/simple-way-to-manage-local-node-module-using-npm-link.html

その他関係ありそうなサイトのメモ

http://mysticdoll.hatenablog.com/entry/%3Fp%3D264

https://engineering.mixmax.com/blog/troubleshooting-npm-link

うーん、まだ、エラーだなぁ

events.js:174
      throw er; // Unhandled 'error' event
      ^ 
TypeError: this.getJhipsterAppConfig is not a function
    at module.exports.readConfig (C:\jhip\generator-jhipster-mymod\generators\app\index.js:16:47)
    at Object.<anonymous> (C:\jhip\generator-jhipster-mymod\node_modules\generator-jhipster\node_modules\yeoman-generator\lib\index.js:424:27)
    at C:\jhip\generator-jhipster-mymod\node_modules\run-async\index.js:25:25
    at new Promise (<anonymous>)
    at C:\jhip\generator-jhipster-mymod\node_modules\run-async\index.js:24:19
    at self.env.runLoop.add.completed (C:\jhip\generator-jhipster-mymod\node_modules\generator-jhipster\node_modules\yeoman-generator\lib\index.js:425:13)
    at runCallback (timers.js:705:18)
    at tryOnImmediate (timers.js:676:5)
    at processImmediate (timers.js:658:5)
Emitted 'error' event at:

このエラーどこで起きているのか、ソースコードをみたら、

generaotrs/app/index.jsにて、

           readConfig() {
               this.jhipsterAppConfig = this.getJhipsterAppConfig();
               if (!this.jhipsterAppConfig) {
                   this.error('Can\'t read .yo-rc.json');
               }
           },

と、なっているが、これが、どこからも設定されてないが、ソースコード上に、

getJhipsterAppConfig()

がなくてはならないつくりなのに、ない。

失われたgetJhipsterAppConfig?()を実装するつもりでイメージをつかむ。

実装のイメージはこんな感じだろう。

function getJhipsterAppConfig(yo-rc_json) {
  // yo-rc_json:yo-rc.jsonをパースしたオブジェクト
  var ret = {};
  ret.jhipsterVersion = 6; //JHipsterのバージョン
  ret.baseName             = jdl.baseName;
  ret.packageName          = jdl.packageName ;
  ret.packageFolder        = jdl.packageFolder;
  ret.clientFramework      = jdl.clientFramework;
  ret.clientPackageManager = jdl.clientPackageManager;
  ret.buildTool            = jdl.buildTool;
  return ret;
}

ソースコードを解析すると、

node_modules\generator-jhipster\cli\import-jdl.jsに、次のコードを見つけた

    getConfig() {
        if (jhiCore.FileUtils.doesFileExist('.yo-rc.json')) {
            logger.info('Found .yo-rc.json on path. This is an existing app');
            const configuration = jhipsterUtils.getAllJhipsterConfig(null, true);
            if (_.isUndefined(this.options.interactive)) {
                logger.debug('Setting interactive true for existing apps');
                this.options.interactive = true;
            }
            this.applicationType = configuration.applicationType;
            this.baseName = configuration.baseName;
            this.databaseType = configuration.databaseType || jhipsterUtils.getDBTypeFromDBValue(this.options.db);
            this.prodDatabaseType = configuration.prodDatabaseType || this.options.db;
            this.devDatabaseType = configuration.devDatabaseType || this.options.db;
            this.skipClient = configuration.skipClient;
            this.clientFramework = configuration.clientFramework;
            this.clientFramework = this.clientFramework || 'angularX';
            this.clientPackageManager = configuration.clientPackageManager;
            if (!this.clientPackageManager) {
                if (this.useNpm) {
                    this.clientPackageManager = 'npm';
                } else {
                    this.clientPackageManager = 'yarn';
                }
            }
        }
    }

もし、修正したいひな形が下記のいずれかの場合

JHIPSTERのサブモジュール拡張機能の通称:ブループリント

を使ったほうがいいかもしれない。

https://www.jhipster.tech/modules/creating-a-blueprint/

ブループリントで拡張可能な、箇所

ブループリントのジェネレータ

ジェネレータもある、質問に答えていけば、ブループリントのジェネレータが生成されるとある。

https://github.com/jhipster/generator-jhipster-blueprint

JDLからのパラメータ

以下は、JHIPSTERのソースコードを読んで、直接改造、するための解析

JDLからのパラメータは、どのように拾えるのか。

テンプレートファイル

テンプレートは、yoというツールを使っていると思う。 JDLをyoで使える形式に変換するのが、JHIPSTERだとすると、テンプレートから、変数を引っ張ってくればいいんじゃなかろうか。

generator-jhipster-vuejs は、node_modulesフォルダに、格納されている さらにその配下の、generatorフォルダを、今回解析してみようと思う。

node_moduleフォルダにある、generatorフォルダが怪しいと思っておこう。

どうやら、jhipsterのコマンドは、

アプリフォルダ/node_moduless/generator-jhipster/cli の

commands.js

ファイルに記載があった。

ただし、ここから、どこかに行くというわけでは、ない。

モジュール側が、リスナーしているようだ。

例えば、import-jdl

というコマンドの場合、次のファイルが、このコマンドを待ち構えるモジュールとして作られている。

import-jdl.js

たぶん、次の文が、リスナーの実装っぽい。確認してないけど。。。GREPしたらここしか、import-jdlの文言含んでなかった。

const statistics = require('../generators/statistics');

statistics.sendSubGenEvent('generator', 'import-jdl');

テンプレートのソースコードを読んで、テンプレートの書き方を逆引きにしてみる

yoのテンプレートの書き方だけでは、役不足だ。JDL言語で簡易に書いた、

クラス図の情報が、どのように、テンプレートの引数にわたってくるのかが、知りたいよね?

だから、テンプレートファイルをみて、解析してみるとする。

とりあえず、エンティティまわりをみてみたいので、下記のファイルを観察してみるとする。

EntityRepository?.java.ejs

コメントの書き方

<%#

-%>

パッケージ名

package <%=packageName%>.repository;

エンティティクラス名

テーブル名とかに使用

<%=asEntity(entityClass)%>

または

<%=entityClass%>

多対多の場合

<%_ if (fieldsContainOwnerManyToMany) { _%>

<%_ } _%>

データベースの種類がsqlの場合

<%_ if (databaseType === 'mongodb') { _%>

<%_ } _%>

SQLのselect句

select <%= entityInstance %> 

リレーションしてる文だけ必要なループ

<% for (idx in relationships) {
   if (relationships[idx].relationshipType === 'many-to-many' && relationships[idx].ownerSide === true) { %>
   left join fetch <%=entityInstance%>.<%=relationships[idx].relationshipFieldNamePlural%><%} }%>",

リレーションのjavadocが定義されているかどうかチェック

for (idx in relationships) {
    if (typeof relationships[idx].javadoc != 'undefined') {
        
    }
}

フィールドでループ

 <% for (idx in fields) {

   } _%>

フィールドのjavadocが定義されているかどうかチェック

  for (idx in fields) {
      if (typeof fields[idx].javadoc != 'undefined') {
          
      }
  }

未確認の引数

fieldsContainBlob

importJsonIgnoreProperties

importApiModelProperty

importJsonIgnore

fieldsContainUUID

prodDatabaseType

hasTextBlob

validation

searchEngine

fieldsContainBigDecimal

jhipster entityとしたときのメッセージの場所

たまたま、みつけたから、メモしておこう。

node_modules\generator-jhipster\generators\entity\prompts.js

にある。

JDLのパーサー

jhiCore.JDLImporter

のimportメソッド

でパースされているようです。

JDLで、認識できる文法を、自分のプロジェクトに合わせれたら、いいんじゃないだろうか?

たとえば、案件ごとに、定義書は、大体似ては、いるが、若干、違う。

それをJDLに、まとめさせて、テンプレートに流すことができたら、すごくよさそうだ。

JDLImporter

node_modules\jhipster-core\lib\jdl\jdl_importer.jsにて

 import() {
   const parsedJDLContent = parseFiles(this.files);

って書いてありました。

function parseFiles(files) {
  return JDLReader.parseFromFiles(files);
}

JDLReaderを調べる必要がありそうです。

const JDLReader = require('../reader/jdl_reader');

とあるので、

アプリのフォルダ/node_modules/jhipster-core/lib/reader/jdl_reader を調べる必要がありそうです。

ちょと、ここで、発見したのですが、removeInternalJDLComments というメソッドで、

JavaDoc?コメントをパース前に、削除して、台無しにしてしまっているようにみえます。

だれか、修正してくれないかぁ。まあ、いいや、解析を進めよう。

const parser = require('../dsl/api');

とあるので、

/node_modules/jhipster-core/jhipster-core/lib/dsl/api.js

を見てみると、

まず、JDL言語の字句解析は、

const lexResult = JDLLexer.tokenize(input);

でおこなっており、

パーサーは、

const parserSingleton = JDLParser.getParser();

でパーサーの配列で取得できるようになっており、

デフォルトでは、progが使われているのが分かった。

で、パースをうごかしているのは、

buildAst(cst);

で、行っているようだ。汎用的につくってあるので、buildAstは、解析しないことにする。

lexer.js

自分用にコマンドを追加するには、

アプリフォルダ/node_modules/jhipster-core\lib/dsl/lexer.js

に、下記のような感じで、認識させる単語を登録させる、必要があるだろう。

たとえば、認識させたい単語が

required

ならば、それをパーサーに伝える際には、

'REQUIRED'

として、伝えたいならば、

createToken({ name: 'REQUIRED', pattern: 'required' });

と書かなくてはならない。

この 'REQUIRED'としたものは、node_modules\jhipster-core\lib\dsl\jdl_parser.jsにて

const LexerTokens = require('./lexer').tokens;

としており、次のように文法のルール定義に記載されている。

{ ALT: () => this.CONSUME(LexerTokens.REQUIRED) },

実務でありそうな要件を考えてみる。

項目の定義がエクセルで、設計済みで、JDLでは、不足しているカラムがあるんだよなー。ってのが、一番ありそうなんじゃ、ないだろうか?

となると、やりたいことは、

カラムと、その中身の定義を、字句解析と、構文解析に追加する、方法が分かりたいということだ。

近そうな記述は、最大値の認識させ方かなと思って、解析を進めてみる。

maxlengthに着目して、解析をする

lexer.jsでのmaxlength

createToken({
  name: 'MAXLENGTH',
  pattern: 'maxlength',
  categories: [tokens.MIN_MAX_KEYWORD]
});

jdl_parser.jsでの、MIN_MAX_KEYWORD

  minMaxValidation() {
    this.RULE('minMaxValidation', () => {
      // Note that "MIN_MAX_KEYWORD" is an abstract token and could match 6 different concrete token types
      this.CONSUME(LexerTokens.MIN_MAX_KEYWORD);
      this.CONSUME(LexerTokens.LPAREN);
      this.OR([{ ALT: () => this.CONSUME(LexerTokens.INTEGER) }, { ALT: () => this.CONSUME(LexerTokens.NAME) }]);
      this.CONSUME(LexerTokens.RPAREN);
    });
  }

jdl_parser.jsでの、minMaxValidation?

 validation() {
   this.RULE('validation', () => {
     this.OR([
       { ALT: () => this.CONSUME(LexerTokens.REQUIRED) },
       { ALT: () => this.CONSUME(LexerTokens.UNIQUE) },
       { ALT: () => this.SUBRULE(this.minMaxValidation) },
       { ALT: () => this.SUBRULE(this.pattern) }
     ]);
   });
 }

  以下、同様に解析していくと、呼び出しているメソッドが、次のようになっていました。

fieldDeclaration

entityBody

テンプレートfield_validators.ejsでのmaxlengthの使われ方

   if (rules.includes('maxlength') && !rules.includes('minlength')) {
       validators.push('@Size(max = ' + field.fieldValidateRulesMaxlength + ')');
   }

どこで、fieldValidateRulesMaxlength? 入れてるの?

自動かなぁ

どうも、テンプレートを見る限りでは、

field.fieldValidateRules, 'maxlength'

が定義されているなら、

field.fieldValidateRulesMaxlength

は、あたかも定義済みだよね~という記述だ。

テンプレートは、どのように管理されているのか?

JHIPSTERは、yoを使っているので、yoでは、どのように、ファイルが生成されているのか、

知りたいところである。

yo での一番基本的な、プロンプト入力 からの、テンプレートを使った生成の書き方。

class extends Generator {
  async prompting() {
    this.answers = await this.prompt([{
      type    : 'input',
      name    : 'title',
      message : 'Your project title',
    }]);
  } 

  writing() {
    this.fs.copyTpl(
      this.templatePath('index.html'),
      this.destinationPath('public/index.html'),
      { title: this.answers.title } // user answer `title` used
    );
  }
}

templatePathに、着目してソースファイルをみると、writeFilesToDisk?というメソッドで、ファイル生成していることがわかった。

例えば自作のjavaのテンプレート追加するにはどうするか

javaのコードのテンプレートと、リネームの設定は、次のフォルダに格納されているみたいだ。

generator-jhipster\generators\entity-server

このフォルダのindex.jsが、files.jsを参照するような形になっていて、このフォルダのtemplateフォルダに、パラメータを渡すように、設定していることがわかったので、

もし、自分でテンプレートを追加したいのであれば、

で、いけそうだ。

参考ページ

https://github.com/SAP/chevrotain/blob/master/examples/lexer/keywords_vs_identifiers/keywords_vs_identifiers.js

パーサー作成ツールキット chevrotain

で、パーサどう書けばいいの?ってなるが、JDLで使っているパーサは、chevrotain

を使っているようだ。

chevrotain のソース

https://github.com/SAP/chevrotain

chevrotain のチュートリアル

https://sap.github.io/chevrotain/docs/tutorial/step0_introduction.html

参考:いろいろなパーサ生成ライブラリの紹介ドキュメント

https://tomassetti.me/parsing-in-javascript/#chevrotain

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2019-07-31 (水) 13:47:12 (24d)