Edited at
AureliaDay 24

Aurelia CLI は一体何をしているのだろう(au run 編)


はじめに

Aurelia CLIをau runコマンドを題材に読み砕いて見ていく、という記事です。

私がサーバーサイドエンジニアでフロントエンドの知識が浅いため、

知り得た情報をなるべく細かく書いています。


Aurelia CLI

https://aurelia.io/docs/cli/basics

Aurelia CLIはAureliaを使用する際に便利なコマンド群を提供します。


 環境

  package
version

aurelia-bootstrapper
2.3.0

aurelia-cli
1.0.0-beta.6


テストプロジェクト作成

まずAureliaのプロジェクトを作成してみます

au newでプロジェクトを作成できます。hereオプションでカレントディレクトリ内に作成してます。

設定は以下の通りです。

tanakeinoMac-puro:aurelia-test tanakei$ au new --here

_ _ ____ _ ___
__ _ _ _ _ __ ___| (_) __ _ / ___| | |_ _|
/ _` | | | | '__/ _ \ | |/ _` | | | | | | |
| (_| | |_| | | | __/ | | (_| | | |___| |___ | |
\__,_|\__,_|_| \___|_|_|\__,_| \____|_____|___|

Which module loader / bundler would you like to use?

1. Webpack (Default)
A powerful and popular bundler for JavaScript
2. CLI's built-in bundler with RequireJS
RequireJS is a mature and stable module loader for JavaScript.
3. CLI's built-in bundler with SystemJS
SystemJS is Dynamic ES module loader, the most versatile module loader for JavaScript

[Webpack]>

What platform are you targeting?

1. Web (Default)
The default web platform setup.
2. ASP.NET Core
A powerful, patterns-based way to build dynamic websites with .NET.

[Web]>

What transpiler would you like to use?

1. Babel (Default)
An open source, standards-compliant ES2015 and ESNext transpiler.
2. TypeScript
An open source, ESNext superset that adds optional strong typing.

[Babel]>

How would you like to setup your template?

1. Default (Default)
No markup processing.
2. Minimum Minification
Removes comments and whitespace between block level elements such as div, blockquote, p, header, footer ...etc
3. Maximum Minification
Removes comments, script & link element [type] attributes and all whitespace between all elements. Also remove attribute quotes where possible.
Collapses boolean attributes.

[Default]>

What css processor would you like to use?

1. None (Default)
Use standard CSS with no pre-processor.
2. Less
Extends the CSS language, adding features that allow variables, mixins, functions and many other techniques.
3. Sass
A mature, stable, and powerful professional grade CSS extension.
4. Stylus
Expressive, dynamic and robust CSS.
5. PostCSS
A tool for transforming CSS with JavaScript.

[None]> 3

Which unit test runners would you like to use?

1. None
Skip testing. My code is always perfect anyway.
2. Karma
Configure your app with Karma and Jasmine
3. Jest
Configure your app with Jest and Jasmine

Select one or more options separated by spaces
> 1

Would you like to configure integration testing?

1. No (Default)
Skip testing. My code is always perfect anyway.
2. Protractor
Configure your app with Protractor

[No]> 1

What is your default code editor?

1. Visual Studio Code (Default)
Code editing. Redefined. Free. Open source. Runs everywhere.
2. Atom
A hackable text editor for the 21st Century.
3. Sublime
A sophisticated text editor for code, markup and prose.
4. WebStorm
A lightweight yet powerful IDE, perfectly equipped for complex client-side development.
5. None of the Above
Do not configure any editor-specific options.

[Visual Studio Code]> 3

Project Configuration

Name: aurelia-test
Platform: Web
Bundler: Webpack
Loader: None
Transpiler: Babel
Markup Processor: None
CSS Processor: Sass
Unit Test Runner: None
Integration Test Runner: None
Editor: Sublime

WARNING: The current directory is not empty. Would you like to create the project in this folder?

1. Yes (Default)
Creates the project structure based on your selections even though the current directory is not empty
2. Restart
Restarts the wizard, allowing you to make different selections.
3. Abort
Aborts the new project wizard.

[Yes]> 1
Project structure created and configured.

Note: lock files are not cross compatible between package managers. Choose Yarn here only if you intend to use Yarn for future package installs. Alternatively, remove either yarn.lock or package-lock.json from the project directory before installing new packages.

Would you like to install the project dependencies?

1. Yes, use Yarn (Default)
Installs all server, client and tooling dependencies needed to build the project using Yarn.
2. Yes, use NPM
Installs all server, client and tooling dependencies needed to build the project using NPM.
3. No
Completes the new project wizard without installing dependencies.

[Yes, use Yarn]> 2

作成後、au runを押すと...

スクリーンショット 2018-12-06 2.10.55.png

動いた!


au runで一体何が起こったのか

ここで本題に入ります。

au runコマンドを使えば簡単にプロジェクトを実行できるのはわかりました。

Webpackもサーバーサイドも何もいじっていないのに、すごいですね。

だが待ってほしい。

そういうものだ、で形付けるにはあまりにも強引だ。私、気になります。


コマンド詳細

まず、ヘルプを見てみます

run --analyze --env value --hmr

Builds the application and serves up the assets via a local web server, watching files for changes as you work.

--analyze - Enable Webpack Bundle Analyzer. Typically paired with --env prod

--env - Sets the build environment.

--hmr - Enable Hot Module Reload

詳細に見ていきましょう


--analyze

これはWebpack Bundle Analyzerというものを有効にしています。

Webpackでバンドルしたファイルの内訳を視覚化してくれるツールのようです。

記事あったので貼っておきます

webpackを可視化するツールを紹介#バンドルファイル内の各パッケージがどのくらいの容量を占めているか知りたい


--env

ビルド環境の設定です。


--hmr

これはHot Module Replacementというものを有効にしています。

これはページをリロードすすることなく更新したコンポーネントを読み込み、描画するという機能です。

通常dev環境ではHot Reloadとなり、こちらはファイル変更時にページをリロードするので、コンポーネントのステートが初期化されます。

これも記事あります

Hot Module Replacementの設定と仕組みを理解する


コードを追ってみる

au runで実行されるコマンドはau newした際に作成されたaurelia_project/tasksに含まれています。

tanakeinoMac-puro:aurelia-test tanakei$ cd aurelia_project/tasks/

tanakeinoMac-puro:tasks tanakei$ ls -la
total 72
drwxr-xr-x 11 tanakei staff 352 12 7 00:07 .
drwxr-xr-x 7 tanakei staff 224 12 6 02:34 ..
-rw-r--r-- 1 tanakei staff 1512 12 6 02:25 build.js
-rw-r--r-- 1 tanakei staff 371 12 5 22:57 build.json
-rw-r--r-- 1 tanakei staff 834 12 6 02:46 environment.js
-rw-r--r-- 1 tanakei staff 616 11 30 23:26 jest.js
-rw-r--r-- 1 tanakei staff 250 11 30 23:26 jest.json
-rw-r--r-- 1 tanakei staff 449 11 30 23:26 karma.js
-rw-r--r-- 1 tanakei staff 247 11 30 23:26 karma.json
-rw-r--r-- 1 tanakei staff 1322 12 6 03:16 run.js
-rw-r--r-- 1 tanakei staff 537 12 7 00:07 run.json

runはrun.js/run.jsonを読んでいるわけです。


run.json

最初にrun.jsonを見てみます


aurelia_project/tasks/run.json

{

"name": "test",
"description": "Builds the application and serves up the assets via a local web server, watching files for changes as you work.",
"flags": [
{
"name": "analyze",
"description": "Enable Webpack Bundle Analyzer. Typically paired with --env prod",
"type": "boolean"
},
{
"name": "env",
"description": "Sets the build environment.",
"type": "string"
},
{
"name": "hmr",
"description": "Enable Hot Module Reload",
"type": "boolean"
}
]
}

なるほど、これはヘルプの内容ですね。つまりこれを編集すると...

"description": "僕の考えた最強呪文",

tanakeinoMac-puro:tasks tanakei$ au

run --analyze --env value --hmr

僕の考えた最強呪文

--analyze - Enable Webpack Bundle Analyzer. Typically paired with --env prod

--env - Sets the build environment.

--hmr - Enable Hot Module Reload

おk。次はrun.jsを見ます。


run.js


aurelia_project/tasks/run.js

import {config} from './build';

import configureEnvironment from './environment';
import webpack from 'webpack';
import Server from 'webpack-dev-server';
import project from '../aurelia.json';
import {CLIOptions, reportWebpackReadiness} from 'aurelia-cli';
import gulp from 'gulp';

function runWebpack(done) {
// https://webpack.github.io/docs/webpack-dev-server.html
let opts = {
host: 'localhost',
publicPath: config.output.publicPath,
filename: config.output.filename,
hot: project.platform.hmr || CLIOptions.hasFlag('hmr'),
inline: true,
port: project.platform.port,
contentBase: config.output.path,
historyApiFallback: true,
open: project.platform.open,
stats: {
colors: require('supports-color')
}
};

if (project.platform.hmr || CLIOptions.hasFlag('hmr')) {
config.plugins.push(new webpack.HotModuleReplacementPlugin());
config.entry.app.unshift(`webpack-dev-server/client?http://${opts.host}:${opts.port}/`, 'webpack/hot/dev-server');
}

const compiler = webpack(config);
let server = new Server(compiler, opts);

server.listen(opts.port, opts.host, function(err) {
if (err) throw err;

reportWebpackReadiness(opts);
done();
});
}

const run = gulp.series(
configureEnvironment,
runWebpack
);

export { run as default };


コマンドで最初に呼ばれる箇所は

const run = gulp.series(

configureEnvironment,
runWebpack
);

export { run as default };

これですね。runをdefaultとしています。

gulp.seriesでfunctionを順番に処理していきます。

最初のconfigureEnvironment

import configureEnvironment from './environment';

environment.jsを読んでいるようです。見てみましょう。


aurelia_project/tasks/environment.js

import project from '../aurelia.json';

import rename from 'gulp-rename';
import {CLIOptions} from 'aurelia-cli';
import gulp from 'gulp';
import fs from 'fs';
import path from 'path';
import through from 'through2';

function configureEnvironment() {
let env = CLIOptions.getEnvironment();

return gulp.src(`aurelia_project/environments/${env}${project.transpiler.fileExtension}`)
.pipe(rename(`environment${project.transpiler.fileExtension}`))
.pipe(gulp.dest(project.paths.root))
.pipe(through.obj(function (file, enc, cb) {
// https://github.com/webpack/watchpack/issues/25#issuecomment-287789288
var now = Date.now() / 1000;
var then = now - 10;
fs.utimes(file.path, then, then, function (err) { if (err) throw err });
cb(null, file);
}));
}

export default configureEnvironment;


aurelia_project/environments/<env>.jsのファイルをenvironment.jsにリネームして、project.paths.rootに出力しています。

project.paths.rootaurelia_project/aurelia.jsonに記載されています


aurelia_project/aurelia.json


...
"paths": {
"root": "src",
...

つまり、初期設定ですとsrc/environment.jsに出力されるわけですね。

run.jsに戻って次はrunWebpackの処理ですね。

これはrun.js内部に実装されています。

順番に見てみます。


aurelia_project/tasks/run.js

  let opts = {

host: 'localhost',
publicPath: config.output.publicPath,
filename: config.output.filename,
hot: project.platform.hmr || CLIOptions.hasFlag('hmr'),
inline: true,
port: project.platform.port,
contentBase: config.output.path,
historyApiFallback: true,
open: project.platform.open,
stats: {
colors: require('supports-color')
}
};

サーバーの設定のようですね。

hot: project.platform.hmr || CLIOptions.hasFlag('hmr'),

CLIOptions.hasFlag('hmr')hmrオプションをチェックして、HMRを使用するか設定しているわけですね。

ただその前に、project.platform.hmrがあります。

これは先程説明したaurelia.jsonから設定を先に取得しています。

次行きます


aurelia_project/tasks/run.js

  if (project.platform.hmr || CLIOptions.hasFlag('hmr')) {

config.plugins.push(new webpack.HotModuleReplacementPlugin());
config.entry.app.unshift(`webpack-dev-server/client?http://${opts.host}:${opts.port}/`, 'webpack/hot/dev-server');
}

hmrオプションが有効のときに、webpackにHotModuleReplacementPluginを追加しています。

次行きます


aurelia_project/tasks/run.js

  const compiler = webpack(config);

let server = new Server(compiler, opts);

server.listen(opts.port, opts.host, function(err) {
if (err) throw err;

reportWebpackReadiness(opts);
done();
});


webpackコンパイラを作成しています。引数のconfig

import {config} from './build';

build.jsに設定されているようですね。見てみましょう。


aurelia_project/tasks/build.js

import webpackConfig from '../../webpack.config';

...
const config = webpackConfig({
production, server, extractCss, coverage, analyze
});
...


webpack.config.jsの設定を持ってきているとうわけですね。

戻ります。

webpackコンパイラと、先程のオプションと共にServerに渡しています。

Serverとは

import Server from 'webpack-dev-server';

なるほど、au runwebpack-dev-serverがwebサーバーとして起動していたわけですね。

その後、server.listenでサーバーを起動して、run.jsの処理は終了です。

reportWebpackReadinesswebpack-reporterという起動ログを表示する関数を叩いています。

https://github.com/aurelia/cli/blob/00ed99754233660f29eb3471f9aad0db9cfe3141/lib/index.js#L12

https://github.com/aurelia/cli/blob/00ed99754233660f29eb3471f9aad0db9cfe3141/lib/build/webpack-reporter.js


おまけ

Aurelia CLIの挙動について、理解が深まったのではないでしょうか。

他のコマンドも同様に見ていくと面白いと思います。

私も暇ができたら見てみます。

よいAureliaライフを!!!

そして

12/24は🎂古河渚🎂の誕生日です。

皆さん、CLANNADを見ましょう。

なぎさ

さようなら