ブラウザでCSV(CP932)⇔XLSXの相互変換

極力サーバーサイドでExcelファイルを処理させたくなくて、(なんちゃって)フロントエンド開発をワチャワチャやっております。

データベースからデータを抜き出すのも、Excelファイルだし、データベースにインポートするデータもExcelファイルです。 もうエクセルを見るのも嫌になってます。

サーバーサイドのプログラムを組む時、データをJSONもしくはCSV形式で扱うと、書くのがラクになります。そこで、ブラウザ上で、CSVとXLSXを相互変換できれば便利だな、と思ったのがきっかけ。

例によって、node.jsモジュールを browserify でワンパッケージにして利用します。(バンドラーって呼ぶみたいです。よく分からん)今回は npmコマンドで exceljs・encoding.js・file-saver の三つのモジュールを取得して、browserify で一つのファイルにパックします。

exceljs は、Excel(xlsx)ファイルを扱うモジュール。ExcelファイルをJavaScriptで扱うライブラリは SheetJS が有名で xlsxだけじゃなくて旧型式のxlsファイルも読むことができて非常に便利なのですが、Pro版じゃないと制限が多いので、最近では ExcelJSの方を使っています。

encoding.js は JavaScript でUTF-8,ShiftJIS(CP932),UTF16などを相互に変換できるライブラリです。ShiftJISのCSVファイルを扱う時に使用します。

file-saverは・・・説明要らないですよね💦 ダウンロードできるようにするヤツです。


動作デモはこちら


手順はいつもどおり。
まずは、package.json 。

今回は 3つのモジュールを一つのファイル(bundle.min.js)にまとめて、実際に使用するときは、そのファイル内(bundle.min.js)に定義されているrequire関数でロードするようにします。

上記package.jsonを適当なディレクトリにおいて下記を実行して、bundle.min.js を生成しておきます。

>> npm install
>> npm run bundle

この bundle.min.js を下記のサンプルのHTMLファイルのように、scriptタグでロードしておきます。

そして・・・肝心の 変換コードは下記のようになります。

特筆すべき点はありませんが・・・encoding-japaneseモジュールで、ShiftJISに変換した後、ShiftJISに変換したものを保存するとき、単純 string型でBlobのコンストラクタに渡すと、再変換されて文字化けを起こしてしまいハマってしまいました。
イマイチあんまり分かってません。

CSVの行区切り、本来は・・・CRLF(0x0D0A)・・・ですよね・・・。workbookからCSVにするとき、worksheetを行毎にループして変換したあと、自前で join(‘\r\n’)とかすればいいんでしょうけど・・・。
workbook.csv.writeBuffer()する際、formatterOptionでrowDelimiterで改行コードを指定すればいいみたい。

フロントエンド、メンドクサイし、ムズカシイし、あんまりやりたくない。。。


動作デモはこちら


Building customized bootstrap theme

なんかタイトルの英語が間違ってたらごめんなさい。
備忘録です。

今職場でAccess + SQLServer2005 で作られている業務用のデスクストップアプリケーションをウェブシステム化する仕事を、本来の業務と並行で行っているのですが、僕が一番苦手なUIデザイン、モバイル携帯向けの含めて、どうしようか・・・と考えたとき、やっぱりネットでの情報が比較的多いbootstrapで・・・となりますよね💦

そこで問題になるのはやっぱりブートストラップ臭が・・・ということでしょうか。。。僕は古いタイプの人間なんで愚直に一個一個CSS、セレクタ、プロパティをガシガシ上書きしてました。これが実に苦痛で。。。

そこで、bootstrap4に移行するついでにSASSでカスタマイズするフローを学ぼう、というわけです(^▽^)/

bootstrap4のテーマの変更(Themingって日本語でなんていうんだ?)の仕方って、ググればググるほど、一体何が正解なんだ?という気になります。僕はGulpとか、gruntとか、webpack とか・・・ググらないと分からない・・・というレベルのオッサンでなんで、今時のトレンデーな、マックブック片手にバリバリ仕事をこなしている、最先端イってるイケてる人たちから見ると、アホかいな?というレベルの備忘録ですので、あらかじめご了承のほど。

余談です。フロントエンド開発が何を意味しているのか僕は正直わかんないです。最近は特についてけないです。Gulpとかgruntとかwebpackとか無縁の職場で、ナニソレ、おいしいの? いや普通に bootstrapの色を変えたり、角丸なくしたり、したいだけなんですぅ。未だに git じゃなくて svn だしぃ。

さて、まずはともかく、本家本元の手順に従いましょう。
ビルドツールのインストールです。https://getbootstrap.com/docs/4.3/getting-started/build-tools/
僕は Windowsユーザーなので、WSL(ubuntu16.04)を使用します。

(1) Node.js のインストール・・・これは適当に入れる。僕は以下の手順でいれます。
一旦aptでインストールし、nパッケージを入れ最新のものに入れ替えた後、apt removeします。

$ sudo apt install -y nodejs npm
$ sudo npm cache clean
$ sudo npm install n -g
$ sudo n stable
$ sudo ln -sf /usr/local/bin/node /usr/bin/node

$ sudo apt purge -v nodejs npm

(2) 上記サイトでは ruby のインストールも書かれていますが、テーマの変更だけしたい場合には必要ないと思います。まぁ、今時の開発者なら ruby は初めからインストールしているでしょうし・・・割愛(^^;

(3) 本家からbootstrapのソースをダウンロードし、適当なディレクトリに展開し、そのディレクトリで npm install で依存しているパッケージをインストールし、npm run css とすると、dist ディレクトリに css がコンパイル、ミニファイされたcssができあがります。

さて・・・ここからが本題です。

本家サイトのTheming(https://getbootstrap.com/docs/4.3/getting-started/theming/)を見るとカスタマイズ方法が書かれていますが・・・おそらくNode.jsに詳しくない、あるいは初めて触る人にとっては、そのページは何の参考にもなりません。。。custom.scssを作って・・・とか書いてますが・・・それで?って感じです。
scssファイルからcssをコンパイルおよびミニファイする具体的な手順とかほとんど書かれていません。

このあたりをググってみても、上記のgulpだの、gruntだの webpackだの、の手順とかがヒットして、オッサンが一番知りたい事の情報がでてきません。単に探し方が悪いのかも。。。
そこで自分なりに調べた結果、以下の手順でカスタマイズするフローに辿り着きました。

以下のフローでは、オリジナルのbootstrapには一切手を付けません。バージョンが上がるといろいろ困りますからね。
具体的なテーマをカスタマイズする方法は、本家サイトの Theming ページを参考にしてもらい、ここでは自分が用意したカスタマイズ用の.scssファイルを、どのように配置して、どのようにビルドするのかを書き留めておきます。

(0) 前準備として・・・

git がインストールされていなければインストールしときます。bootstrapのソースは git clone で取るようにしますんで。

(1) 作業用に適当なディレクトリを作り・・・

以下のようにディレクトリ・ファイルを配置します。。。各ファイル(a,b,c1~3)については後述します。

$ tree -n
.
├── babel.config.js  (a)
├── bootstrap (bootstrapのソースを置くディレクトリ)
├── dist (コンパイルされた css/js の置き場)
│   ├── css
│   └── js
├── package.json (b)
└── scss
    ├── custom-bootstrap-grid.scss (c1)
    ├── custom-bootstrap-reboot.scss (c2)
    └── custom-bootstrap.scss (c3)

(a) babel.config.js

bootstrapのソースにある、.babelrc.js をコピーしてこの名前にリネームします。JSファイルをコンパイルする際に単純にコピーして配置しただけだとエラーになってしまいました。理屈は知りません。すみません。npm-babelのサイトを斜め読みしたら、このファイル名に行きつきました💦

(b) package.json

これもbootstrapのソースにある、package.json をそのままコピーし、カスタマイズ用に修正しました。僕にとっては要らない機能もあるので逐一調べて不要なものを削除しまくり、以下のコードに行きつきました。内容については・・・よく分かりません。調べてください。すみません。

package.jsonは、npm(node package manager)が管理するファイルで、scriptsっていうキーにコマンド名、値に実行するコマンドラインを記述していくみたいです。
npm run コマンド名 で、そのコマンド名の値のコマンドラインが実行される、みたいな。makeみたいなもんか?

長くなりましたが、コピーした package.jsonファイル内のパス指定されている部分を修正したり、単体テストとかlintとか、サーバー機能、ドキュメントの構築などのビルドツールは基本必要ないので、片っ端から削除しました。テーマのカスタマイズ用にcss/jsのビルドさえできればいいという事にフォーカスしています。

通常はファイルを修正したら即反映されるような監視タスクを入れるのが開発のセオリーみたいなのですが・・・あれもこれもとやってると、結局、開発環境構築オタクになってしまうんで、やめときます。開発環境を作りたいんじゃなくて・・・コードを書くのが目的!

{
  "name": "custom-bootstrap",
  "description": "custom bootstrap4",
  "scripts": {
    "clone-bootstrap": "git clone https://github.com/twbs/bootstrap.git -b v4.3.1 --depth 1 bootstrap",
    "css": "npm-run-all css-compile css-prefix css-minify",
    "css-main": "npm-run-all css-compile-main css-prefix-main css-minify-main css-copy",
    "css-compile": "npm-run-all --parallel css-compile-*",
    "css-compile-main": "node-sass --output-style expanded --source-map true --source-map-contents true --precision 6 scss/custom-bootstrap.scss dist/css/bootstrap.css && node-sass --output-style expanded --source-map true --source-map-contents true --precision 6 scss/custom-bootstrap-grid.scss dist/css/bootstrap-grid.css && node-sass --output-style expanded --source-map true --source-map-contents true --precision 6 scss/custom-bootstrap-reboot.scss dist/css/bootstrap-reboot.css",
    "css-minify": "npm-run-all --parallel css-minify-*",
    "css-minify-main": "cleancss --level 1 --format breaksWith=lf --source-map --source-map-inline-sources --output dist/css/bootstrap.min.css dist/css/bootstrap.css && cleancss --level 1 --format breaksWith=lf --source-map --source-map-inline-sources --output dist/css/bootstrap-grid.min.css dist/css/bootstrap-grid.css && cleancss --level 1 --format breaksWith=lf --source-map --source-map-inline-sources --output dist/css/bootstrap-reboot.min.css dist/css/bootstrap-reboot.css",
    "css-prefix": "npm-run-all --parallel css-prefix-*",
    "css-prefix-main": "postcss --config bootstrap/build/postcss.config.js --replace \"dist/css/*.css\" \"!dist/css/*.min.css\"",
    "js": "npm-run-all js-compile js-minify js-move",
    "js-main": "npm-run-all js-lint js-compile js-minify-main",
    "js-compile": "npm-run-all --parallel js-compile-*",
    "js-compile-standalone": "rollup --environment BUNDLE:false --config bootstrap/build/rollup.config.js --sourcemap",
    "js-compile-bundle": "rollup --environment BUNDLE:true --config bootstrap/build/rollup.config.js --sourcemap",
    "js-compile-plugins": "node bootstrap/build/build-plugins.js",
    "js-minify": "npm-run-all --parallel js-minify-main",
    "js-minify-main": "npm-run-all js-minify-standalone js-minify-bundle",
    "js-minify-standalone": "uglifyjs --compress typeofs=false --mangle --comments \"/^!/\" --source-map \"content=bootstrap/dist/js/bootstrap.js.map,includeSources,url=bootstrap.min.js.map\" --output bootstrap/dist/js/bootstrap.min.js bootstrap/dist/js/bootstrap.js",
    "js-minify-bundle": "uglifyjs --compress typeofs=false --mangle --comments \"/^!/\" --source-map \"content=bootstrap/dist/js/bootstrap.bundle.js.map,includeSources,url=bootstrap.bundle.min.js.map\" --output bootstrap/dist/js/bootstrap.bundle.min.js bootstrap/dist/js/bootstrap.bundle.js",
    "js-move": "cross-env-shell shx mkdir -p dist/js && cross-env-shell shx cp -r bootstrap/dist/js dist/",
    "dist": "npm-run-all --parallel css js",
    "dist-clean": "cross-env-shell shx rm -f dist/{js,css}/*"
  },
  "devDependencies": {
    "@babel/cli": "^7.2.3",
    "@babel/core": "^7.2.2",
    "@babel/plugin-proposal-object-rest-spread": "^7.3.2",
    "@babel/preset-env": "^7.3.1",
    "autoprefixer": "^9.4.7",
    "babel-eslint": "^10.0.1",
    "babel-plugin-istanbul": "^5.1.0",
    "bundlesize": "0.15.3",
    "clean-css-cli": "^4.2.1",
    "cross-env": "^5.2.0",
    "find-unused-sass-variables": "^0.3.2",
    "glob": "^7.1.3",
    "node-sass": "^4.11.0",
    "npm-run-all": "^4.1.5",
    "postcss-cli": "^6.1.1",
    "rollup": "^1.1.2",
    "rollup-plugin-babel": "^4.3.2",
    "rollup-plugin-commonjs": "^9.2.0",
    "rollup-plugin-node-resolve": "^4.0.0",
    "shelljs": "^0.8.3",
    "shx": "^0.3.2",
    "uglify-js": "^3.4.9"
  },
  "engines": {
    "node": ">=6"
  }
}

上記 scripts を見てもらえれば、だいたい何をやってるのか分かると思います。
devDependencyは・・・必要なnodeプログラムの一覧?で、npm install すると、これらのプログラムが node_modules ディレクトリにインスコされるみたいですぅ。知らんけど。知らんけど。

(c1 ~ c3)bootstrapの下記ファイルをそれぞれインポートしてカスタマイズするファイルです。

  • bootstrap/scss/bootstrap.scss
  • bootstrap/scss/bootstrap-grid.scss
  • bootstrap/scss/bootstrap-reboot.scss

これは Theming でも説明されています。各ファイルの内容は・・・

/*******************************************************************************
 ファイル名: ./scss/custum-bootstrap.scss
ここに bootstrap本体の変数SASS を記述(上書き)していく。
その方法は、Theming を参照のこと。
*******************************************************************************/
$primary:  orange; 
.
.
.

/* ソース本体をインポート */
@import "../bootstrap/scss/bootstrap";

./scss/custom-bootstrap-grid.scss、./scss/custom-bootstrap-reboot.scss も同様です。

これらの./scss/custum-bootstrap*.scssを自分用にカスタマイズを施したあと、最後はビルドします。
カスタマイズは、主にbootstrap/scss/_variables.scss の中で定義されている変数を変更したりするのがカスタマイズの中心になってくると思います。もちろん、独自のスタイルを定義したければ、ここに書きます。

(2) コンパイル処理

./scss/custom-bootstrap*.scss の編集が終わったら、ビルドします。
順番としては、bootstrapソースを取得してから、npm run dist します。
distは、js,cssを一緒にやる感じです。

# まず、bootstrapのソースをbootstrapディレクトリに取得し、格納します。
# package.json を見ればわかりますが、git clone しているだけです。
$ npm run clone-bootstrap

# もし css/js すべてをビルドしたい場合は、dist
$ npm run dist

# css だけ欲しかったら css
$ npm run css

bootstrapをcloneするとき、v4.3.1のブランチを取得していますが、最新のものが必要であれば、”-b v4.3.1″の部分を消す。また”–depth 1″を消すと過去の履歴全部取得するので異常に時間がかかりますので消さない方がいいと思います。

なお、自分でソースをダウンロードする場合は、clone-bootstrapは実行する必要ありません。

lintとかテストとか一切省いているのでエラーが出たら・・・自力で何とかする!