かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

Webpack production mode の時だけ babel-preset-minify で console.log を消したい

開発中は console.log で色々確認したいけど、ESLint で console.log が残ってるとエラーになるようにしてると、特に雑でいい個人開発だと都度消してってのがメンドイので production の時だけ console.log を削除してしまいたかった。

webpack の uglifyjs-webpack-plugin で実現できていたみたいだけど、リポジトリが archived になっていたので、コレは使わず今回は babel の babel-preset-minify を使って console.log を削除するようにしてみた。

環境
  • webpack ^5.6.0
  • @babel/core ^7.12.10
  • babel-loader ^8.2.2
  • babel-preset-minify ^0.5.1

npm scripts

"scripts": {
  "start:dev": "webpack serve --mode development",
  "start:prod": "webpack serve --mode production",
  "build": "webpack --mode production"
}

npm script でそれぞれの場合にあった mode をオプションで指定して実行するようにした。

babel-preset-minify で console.log を削除する

webpack.config.js

module.exports = {
  // …
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: ['babel-loader'],
      },
    ],
  },
}

babel.config.json

{
  "presets": [
    "@babel/env",
    [
      "minify",
      {
        "removeConsole": {
          "exclude": [ "error", "info" ]
        }
      }
    ]
  ]
}

これで console.log が削除されるようになりました。
JavaScript の minify は --mode=production の時のみ実行されるみたいなのですが、console.log の削除は mode に関係なく production でも development でも削除されてしまいました。
これでは 開発中 (--mode=development) のとき困るので、production の時のみ removeConsole のオプションを付けるように修正する必要があります。

production mode の時だけ removeConsole オプションを付けて console.log を削除する

babel 設定は JavaScript なら Config Function API を使って色々と設定を取り出して設定を変更することができるようです。

module.exports = function(api) {
  return {};
};

cf. [https://babeljs.io/docs/en/config-files#config-function-api:title]

Config Function APIapi.env() を使うと NODE_ENV の値を利用することができます。babel.config.jsonbabel.config.js にリネームして NODE_ENVproduction の時のみ removeConsole を使うように変更します。

babel.config.js

module.exports = (api) => {
  const prodMode = api.env('production');

  return {
    "presets": [
      "@babel/env",
      [
        "minify",
        prodMode && {
          "removeConsole": {
            "exclude": ["error", "info"]
          }
        }
      ].filter(Boolean)
    ]
  }
};

これで --mode=production の時だけ console.log が…となるつもりでしたが babel.config.js に渡ってくる api.env が常に development になっていて意図したとおりに動作しません。

これは npm script で設定してある webpack serve --mode production の モードは process.env.NODE_ENV に設定される訳ではないので、babel の設定内で取れる api.env() はデフォルトの development になってしまっていたという事でした。
npm script に NODE_ENV= のオプションを付けても良いのですが、--mode と二重になってしまってあまりイケてない気がしたので babel-loader を通じて babel を呼び出す webpack の設定で --mode から NODE_ENV を設定するようにします。

webpack.config.js で --mode オプションを使う

webpack の設定内でコマンドから渡されるオプションを取得するには (env, argv) => { return {/* webpack.config */} } の形にすることで argv 引数を使って値を取得することができます。

webpack.config.js

module.exports = (env, argv) => {
  const prodMode = argv.mode === 'production';
  // --mode=production の時、NODE_ENV を設定する
  if (prodMode) {
    process.env.NODE_ENV = 'production';
  }
  
  return {
    // …
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          use: ['babel-loader'],
        },
      ],
    },
  };
}

webpack で NODE_ENV が設定されてから babel が呼び出されるので、これで --mode=production の時のみ babel の babel-preset-minify のオプションが切り替わり console.log を削除できるようになりました!
₍ ᐢ. ̫ .ᐢ ₎ ヤッヤネ!

所感

昔やった気がする…って思ったらやっぱり書いてた。
この時は uglifyjs-webpack-plugin と同じような動きをする terser-webpack-plugin を使う方法を書いてた。
今見ると config 見づらいし、今回は webpack の --mode オプションを使って babel の設定を分岐させられるようにする学びが合ったのでヨシ!

2021年最初だし去年読んでよかった本とか買って良かったものとか書こうかと思ったけど、本全然読めてなかったし特に買ったものもなかった… 去年何してたんだろう… 虚無か?

それでは今年も Eigðu góðann dag〜 ; )


[参考]

今年もサムネのためにアイカツ!を意味もなく貼っていくぞ!

JavaScript で おしえて A to Z

2020年最後なのでネタ投稿です。

charCodeAt を使って A-Z の文字コードを作る

charCodeAt() メソッドは、指定された位置にある UTF-16 コードユニットを表す 0 から 65535 までの整数を返します。
cf. String.prototype.charCodeAt() - JavaScript | MDN

'A'.charCodeAt(0);
// => 65

fromCharCode 文字コードから文字に戻す

アルファベットの文字コードを作成して String.fromCharCode を使って文字に戻す

String.fromCharCode() 静的メソッドは、指定された UTF-16 コードユニットの並びから生成された文字列を返します。
cf. String.fromCharCode() - JavaScript | MDN

String.fromCharCode(65);
// => "A"

A-Z を作成する

# A-Z の文字コード
Array.from({length: 26}, (_,i) => 'A'.charCodeAt(0) + i);
// => [65, 66, 67, …, 90]

# A-Z の文字列にする
String.fromCharCode(…Array.from({length: 26}, (_,i) => 'A'.charCodeAt(0) + i));
// => "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

# A-Z の配列にする
Array.from({length: 26}, (_,i) => String.fromCharCode('A'.charCodeAt(0) + i));
// => ["A", "B", "C", …, "Z"]

おわり

おまけ

`19:8:2:16:14:23`.split(':').reduce(
  (a,b) => ([a[b], ...a]),
  Array.from({length: 26}, (_,i) => String.fromCharCode('A'.charCodeAt(0)+i))
).reverse().slice(26).join('');

THANKS

 
良いお年を〜! 幅無いっすね☆


[参考]

ハンズオンNode.js

ハンズオンNode.js

  • 作者:今村 謙士
  • 発売日: 2020/11/17
  • メディア: 単行本(ソフトカバー)

おしえて A to Z

おしえて A to Z

劇場版アイカツ! 豪華版 [Blu-ray]

劇場版アイカツ! 豪華版 [Blu-ray]

  • 発売日: 2015/06/02
  • メディア: Blu-ray

2021年もアイカツ!ダーッシュ!!!

JavaScript Object <---> Array の操作 Object.keys(), Object.values(), Object.entries(), Object.fromEntries()

Object.keys()

オブジェクトのキー一覧が配列で取れる

const Idols = {
  'cute': ['Ichigo', 'Sakura', 'Akari', 'Madoka', 'Maria'],
  'cool': ['Aoi', 'Yurika', 'Shion', 'Sumire', 'Seira'],
  'sexy': ['Ran', 'Mitsuki', 'Hikari', 'Juri', 'Sora'],
  'pop': ['Otome', 'Kaede', 'Mikuru', 'Hinaki', 'KoKone']
};

Object.keys(Idols)
// => ["cute", "cool", "sexy", "pop"]

Object.values()

オブジェクトの値の一覧が配列で取れる

const Idols = {
  'cute': ['Ichigo', 'Sakura', 'Akari', 'Madoka', 'Maria'],
  'cool': ['Aoi', 'Yurika', 'Shion', 'Sumire', 'Seira'],
  'sexy': ['Ran', 'Mitsuki', 'Hikari', 'Juri', 'Sora'],
  'pop': ['Otome', 'Kaede', 'Mikuru', 'Hinaki', 'KoKone']
};

Object.values(Idols);
/*
[
  ["Ichigo", "Sakura", "Akari", "Madoka", "Maria"],
  ["Aoi", "Yurika", "Shion", "Sumire", "Seira"],
  ["Ran", "Mitsuki", "Hikari", "Juri", "Sora"],
  ["Otome", "Kaede", "Mikuru", "Hinaki", "KoKone"]
]
*/

Object.entries()

Object の {key: value}[key, value] な配列に変換する。
※ 変換された配列の順番は元のオブジェクトの key の並び順を担保しない
配列の形になるので map や sort で処理したい時に便利かも。

例えば API からこんな感じの JSON データが帰ってきたものを no 順に並べたいとか

// API から取得されたデータ
const Idols = {
  'hash1': { no: 1, name: 'Ichigo' },
  'hash7': { no: 7, name: 'Yurika' },
  'hash6': { no: 6, name: 'Hinaki' },
  'hash4': { no: 4, name: 'Akari' },
  'hash5': { no: 5, name: 'Sumire' },
  'hash9': { no: 9, name: 'Mikuru' },
  'hash8': { no: 8, name: 'Mituki' },
  'hash2': { no: 2, name: 'Aoi' },
  'hash3': { no: 3, name: 'Ran' },
};

const list = Object.entries(Idols);
/*
[
  ['hash1', { no: 1, name: 'Ichigo' }],
  ['hash7', { no: 7, name: 'Yurika' }],

]
*/

// hash を id に変換
const listFormat = (list) => {
  return list.map((item) => ({
    id: item[0],
    ...item[1]
  }));
};
// リスト内のオブジェクトのキーでソート
const sortByKey = (list) => (key) => {
  return list.sort((a, b) => a[key] - b[key]);
};
sortByKey(listFormat(list))('no');
/*
[
  {id: "hash1", no: 1, name: "Ichigo"},
  {id: "hash2", no: 2, name: "Aoi"},
  {id: "hash3", no: 3, name: "Ran"},
  {id: "hash4", no: 4, name: "Akari"},
  {id: "hash5", no: 5, name: "Sumire"},
  {id: "hash6", no: 6, name: "Hinaki"},
  {id: "hash7", no: 7, name: "Yurika"},
  {id: "hash8", no: 8, name: "Mituki"},
  {id: "hash9", no: 9, name: "Mikuru"}
]
*/

₍ ᐢ. ̫ .ᐢ ₎👌

Object.fromEntries()

これはオブジェクトの操作ではないけれど [[key1, value1], [key2, value2], ...] な配列を {key1: value1, key2: value2, ...} なオブジェクト形式に変換する。
Object.entries() で変換したものをもとに戻すイメージ。

// {id: name} なオブジェクトにしたい
const Idols = [
  { id: 1, name: 'Ichigo' },
  { id: 7, name: 'Yurika' },
  { id: 6, name: 'Hinaki' },
  { id: 4, name: 'Akari' },
  { id: 5, name: 'Sumire' },
  { id: 9, name: 'Mikuru' },
  { id: 8, name: 'Mituki' },
  { id: 2, name: 'Aoi' },
  { id: 3, name: 'Ran' },
];

const formatList = Idols.map(({id, name}) => [id, name]);
/*
[
  [1, 'Ichigo'],
  [7, 'Yurika'],
  ...
]
*/
Object.fromEntries(formatList);
/*
{
  1: "Ichigo"
  2: "Aoi"
  3: "Ran"
  4: "Akari"
  5: "Sumire"
  6: "Hinaki"
  7: "Yurika"
  8: "Mituki"
  9: "Mikuru"
}
*/

₍ ᐢ. ̫ .ᐢ ₎👌

配列の3番目以降の要素は切り捨てられる
Object.fromEntries([[1, 2, 3]]);
// => {1: 2}
同じキーがあると配列のインデックスが後ろにある要素が残る
Object.fromEntries([['cute', 'Ichigo'], ['cute', 'Akari']])
// => {cute: "Akari"}

hasOwnProperty はどうなっているか?

Object.keys()Object.hasOwnPropertytrue のものだけが列挙される
const obj = new Object({foo: 1});
const myObj = Object.create(obj);
myObj.bar = 2;
for (key in myObj) { console.log(key, myObj.hasOwnProperty(key)) }
// => bar true
// => foo false
Object.keys(myObj);
// => ["bar"]
Object.values()Object.hasOwnPropertytrue のものだけが列挙される
const obj = new Object({foo: 1});
const myObj = Object.create(obj);
myObj.bar = 2;
for (key in myObj) { console.log(key, myObj.hasOwnProperty(key)) }
// => bar true
// => foo false
Object.values(myObj);
// => [2]
Object.entries()Object.hasOwnPropertytrue のものだけが列挙される
const obj = new Object({foo: 1});
const myObj = Object.create(obj);
myObj.bar = 2;
for (key in myObj) { console.log(key, myObj.hasOwnProperty(key)) }
// => bar true
// => foo false
Object.entries(myObj);
// => [["bar", 2]]

 
おわり


[参考]

ハンズオンJavaScript

ハンズオンJavaScript