tohokuaikiのチラシの裏

技術的ネタとか。

Confluenceの添付ファイルに擬似的にプロパティを増やす方法

Confluenceの添付ファイルのプロパティありますよね。

f:id:tohokuaiki:20190822120855p:plain

でも、これだと1つだけしか入れられないので

f:id:tohokuaiki:20190822120948p:plain

こんな感じにしたい!っていう要望を受けて簡単にJavaScriptで擬似的にコメント欄に無理やり入れ込んだ。

使い方

gist71f1c384c19ddda6f56250dbaa6c888c

で、これをどっかにアップして・・・まぁ、とりあえずなら
https://gist.githubusercontent.com/tohokuaiki/71f1c384c19ddda6f56250dbaa6c888c/raw/582465e4860a163c44243b81376fae93a90e5ede/confluence-comment-separator.js
を使っても構わないけど、

<script>
var sNew = document.createElement("script");
sNew.async = true;
sNew.src = 'https://gist.githubusercontent.com/tohokuaiki/71f1c384c19ddda6f56250dbaa6c888c/raw/582465e4860a163c44243b81376fae93a90e5ede/confluence-comment-separator.js'
var s0 = document.getElementsByTagName('script')[0];
s0.parentNode.insertBefore(sNew, s0);
</script>

というのを、ConfluenceのカスタムHTMLでヘッダに入れてやると添付ファイルのプロパティページで使用できる。

あ、でも、これConfluenceの6.6で作ったのでうまく動かなかったらセレクタ あたりを変更する必要があるかも。

項目名を変更したい

11行目の、extraAttachComments を変更するとできる。

var extraAttachComments = {
            title: '使用論文名',
            comment: 'コメント',
            url: '論文URL'
          };

のキーを変えなければ、既に登録した後で入力時のラベル名だけを変更することもできる。

理屈

1つあるコメント欄に複数のプロパティを入れてるだけ。表示時と保存時にセパレータである '<:>'と'|+|' で分割・結合してるだけなので、このセパレータが出てくると不具合が起こる。

セパレータを変えたかったら、4行目・5行目を適当に変更する。

Re: 元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲

こちらの記事を読んだら、ブコメに挙がってる方が「あぁ~~~」ってなったのでまとめました。 dankantakeshi.hatenablog.com

以下、引用とYouTubeでの例示

自分が聞いてみて「うーん」と思ったのだけです。

元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

吉本新喜劇テーマ曲「Somebody Stole My Gal」も元はタイトル通り失恋ソングです(吉本抜きでもそんな風にはあまり聞こえないが)

2019/07/26 08:40
b.hatena.ne.jp


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

バラエティ番組見ないのでここに挙がってる曲のイメージは全く分からないけど、デイ・ドリーム・ビリーバーの歌詞はセブンのイメージでいいのかとは思う

2019/07/26 08:20


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

逆パターンとしてあまりに聞き慣れたアニメやゲームの挿入歌やSEが一般的な番組に使われるパターンがあり、ものすごい気まずい思いをする。具体的には平成教育委員会のファンファーレ的なとこにバーチャロンOMGの勝利S

2019/07/26 08:45


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

HⅡHの「feels like HEAVEN」(リングの「来る、きっと来る」のやつ)

2019/07/26 09:15


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

そりゃ「タブー」やろ、とおもたらWikipediaでも&quot;ただしドリフターズによるヒットから40年以上を経たこんにちでは、性的、あるいは滑稽なイメージは薄れてきている&quot;という扱い。なんと!//すでに英語版からは官能的だとか

2019/07/26 09:18


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

ビタースウィートサンバ

2019/07/26 10:24
オールナイトニッポンのオープニング曲ですね。


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

アメリカ横断ウルトラクイズのテーマ(そんなのないです)ドリフのピンクパンサー(ないです)

2019/07/26 10:36


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

ジプシーキングスがビールを連想させるのが入ってないだと

2019/07/26 10:48


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

Roundaboutのイントロはもう次回に続きそうなイメージしか沸かない...

2019/07/26 11:18
Yesの名曲ですね。


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

とある俳優さんのインタビューで,離婚が成立した日,妻に引き取られる子供が泣いてすがった時にたまたま流れていた曲がリパブリック賛歌だったせいでヨドバシカメラに入れなくなった(泣いちゃうから)と話していた

2019/07/26 11:45


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

ヴェルディアイーダ凱旋行進曲→サッカー男子日本代表 モーツァルトのホルン協奏曲第1番第1楽章→黄金伝説 リヒャルト・シュトラウスドンファンハマタがドアを開ける

2019/07/26 11:52


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

オリーブの首飾りは別に手品の曲ではないらしい

2019/07/26 11:59


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

Deep PurpleのBurnとタマホーム。冷静に考えるとろくでもない組み合わせだが。

2019/07/26 12:03


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

もうちょっと面白い話かと思ったら、全部テレビのテーマ曲だった。ツァラトゥストラ斯く語りきとかワルキューレの騎行とかさー

2019/07/26 12:05
2001年宇宙の旅

地獄の黙示録でキルゴア中佐がベトナム農村を攻撃するシーンのBGMですね。


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

BEYONDの「長城」の前奏だな。あれなんて、電波少年のジングルだと思ってる人すらいるだろ。

2019/07/26 12:16


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

スタートレックのテーマを聞くとニューヨークに行きたいかー!と言いたくなる、みたいな。

2019/07/26 12:24
アメリカ横断ウルトラクイズのオープニング曲


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

映画「第三の男」ハリー・ライムのテーマがサッポロビールのイメージで、恵比寿駅の発車メロディに採用されたやつ。

2019/07/26 12:31


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

重いコンダラみたいな話かと思ったら恋のから騒ぎ嵐が丘の話だった

2019/07/26 12:43


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

映画『バックドラフト』のテーマ曲を聴くと、いまだに『料理の鉄人』を思い出すw

2019/07/26 12:50


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

サル、ゴリラ、そしてチンパンジー

2019/07/26 13:27


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

Ievan Polkka → ねぎ

2019/07/26 14:22


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

&quot;Holding Out For A Hero&quot;。「鍛え抜かれた体のヒーロー、私に現れて!」という共感度MAXの歌なのに、日本だとラグビーで部活のヒーローの歌。/小柳ルミ子のカバーが原曲の歌詞も雰囲気もバッチリ(それ以上)。オススメ!

2019/07/26 16:17


元々の楽曲の歌詞やイメージとは別のイメージがついてしまった曲・もうそれにしか聴こえない曲 - Jailbreak

アートオブノイズのLegsはハンドパワーになってしまった。

2019/07/26 16:39

gulp4を使ってCache bustingする方法(その2・実際にQueryStringを付ける編)

やること

ということでCSSJavaScriptは1枚にまとまったのでHTMLに対してそれぞれをLinkしているタグのsrc/hrefにQueryStringを付けてやる。

<head>
  <link href="css/main.css" rel="stylesheet">
  <script src="js/main.js"></script>
</head>

<body>
  <img src="images/sample.gif">
</body>

<head>
  <link href="css/main.css?v=(main.cssのMD5値)" rel="stylesheet">
  <script src="js/main.js?v=(main.jsのMD5値)"></script>
</head>

<body>
  <img src="images/sample.gif?v=(sample.gifのMD5値)">
</body>

という感じです。

で、そもそもQueryStringを付けてうまくいくのか?

この記事 blog.open.tokyo.jp を読むと、

GETパラメータの付加は推奨されない 前記の対応のうち、GETパラメータにリビジョン番号を追加する方法はあまり推奨されません(参照:High Performance Web Sites)。 この方法は、ブラウザやWebサーバがキャッシュを利用しないため、サイトの負荷につながります。

とあった。んー、と思って調べると stackoverflow.com で同じような質問をしている人が。回答の返信の所だけど、「 stevesouders.com/blog/2008/08/23/ ではQueryString付きだとブラウザやCDNがキャッシュしてくれない」とあるよ…と。

Steve Souders: "To gain the benefit of caching by popular proxies, avoid revving with a querystring and instead rev the filename itself." The full explanation can be found here: stevesouders.com/blog/2008/08/23/… – lao Feb 18 '16 at 13:48 24

に対して「それは古い情報だ。CDNやProxyは対応している」と。CDNが対応しているならブラウザもだろう。Chromeで試すとQueryString付きのCSSは「Provisional headers are shown」と出てるのでキャッシュされている。

That blog post is approaching a decade old now. Do you think that cache providers and CDNs have yet to accommodate it? Squid seems to be able to cache documents with query strings now. – jeteon Mar 9 '16 at 22:44

で、先ほどのブログも、その根拠になっているhttps://www.amazon.co.jp/High-Performance-Web-Sites-Essential/dp/0596529309/は2007年の発行だった。

で、QueryString付きにしてくれるのがgulp-rev-append

だったのだけど、glup-rev-appendはgulp-utilに依存してるのでうーん…とりあえず、gulp-utilのFileライブラリを使ってないのでこれで試したところ、なんかイマイチだった。

というのは、変更したいimg/scriptなどには、?rev=@@hashをつけておく必要があって、「うーん、それを逐一つけるのはなぁ」という感じ。

出力側SCSSを更にCache busterしたところ

上手く動かなかった。 パッと思ったのは、無限ループになってるな…と。出力したCSSwatchしてCacsh busting処理をする…となるとまたそれがwatch対象になる。2つできてしまうのはイマイチだなー。

ということで、SCSSからのコンパイル時に引っかけるようにした。

これをあまりしたくなかった理由は、gulpのSCSSではなくデザイナーが別途SCSSコンパイラ使ってる時*1に困るなーと思ったから。コンパイル済みのCSSに対してCache Bustingしたかったため。

しかし、どう考えてもうまくいかなかったので、gulpでSCSSコンパイルすることにして、こんな感じのgulpファイルになった。

HTMLのcache buster処理はgulp-rev-appendを参考にした。

var gulp        = require('gulp');
var connect     = require('gulp-connect');
var uglify      = require("gulp-uglify");
var plumber     = require("gulp-plumber");
var concat      = require("gulp-concat");
var sourcemaps  = require("gulp-sourcemaps");
var sass        = require('gulp-sass');
var minimist    = require("minimist");
var gulpif      = require('gulp-if');
var eventstream = require('event-stream');
var postcss     = require('gulp-postcss');
var cachebuster = require('postcss-cachebuster');
const crypto    = require('crypto');
const fs        = require('fs');
const path      = require('path');
const cheerio   = require('cheerio');

var options = minimist(process.argv.slice(2), {
  string: 'env',
  default: { env: 'production' }
});

const config = {
    production: options.env !== 'dev',
    sourceMaps: options.env === 'dev'
};

function htmlrev(){
    return eventstream.map(function(file, cb) {
        var $ = cheerio.load(file.contents.toString(),
                             { decodeEntities: false });
        $('link[rel="stylesheet"],script[src],img').each(function(i, ele) {
            let attrname = "";
            let hash;
            switch (ele.name.toLowerCase()){
              case 'link':
                attrname = 'href';
                break;
              case 'img':
              case 'script':
                attrname = 'src';
                break;
            }
            if (attrname) {
                let src = $(ele).attr(attrname);
                if (!src.match(new RegExp('^https?:\/\/'))) {
                    var normPath = path.normalize(src);
                    if (normPath.indexOf(path.sep) === 0) {
                        dependencyPath = path.join(file.base, normPath);
                    }
                    else {
                        dependencyPath = path.resolve(path.dirname(file.path), normPath);
                    }
                    let hasQS = dependencyPath.lastIndexOf("?");
                    if (hasQS >= 0){
                        dependencyPath = dependencyPath.substring(0, hasQS);
                    }
                    // replace src => htdocs
                    dependencyPath = dependencyPath.replace(
                        path.sep+'src'+path.sep,
                        path.sep+'htdocs'+path.sep);
                    try {
                        data = fs.readFileSync(dependencyPath);
                        hash = crypto.createHash('md5');
                        hash.update(data.toString(), 'utf8');
                        $(ele).attr(attrname,
                                    $(ele).attr(attrname) + '?rev=' + hash.digest('hex'));
                    }
                    catch(e) {
                        // fail silently.
                        console.log(e.message);
                    }
                }
            }
        });
        file.contents =
          new Buffer.from($.root().html());
        cb(null, file);
    });
}

gulp.task('javascript', function(cb){
    gulp.src(['./src/javascripts/vendor/*.js', './src/javascripts/*.js'])
      .pipe(plumber())
        .pipe(gulpif(config.sourceMaps, sourcemaps.init()))
          .pipe(concat('dest.js'))
            .pipe(uglify())
              .pipe(gulpif(config.sourceMaps, sourcemaps.write()))
                .pipe(gulp.dest("htdocs/js/"));
    cb();
});

gulp.task('sass', function (cb) {
    gulp.src(['./src/scss/vendor/*.scss', './src/scss/*.scss'])
      .pipe(plumber())
        .pipe(gulpif(config.sourceMaps, sourcemaps.init()))
          .pipe(concat('style.css'))
            .pipe(sass({
              outputStyle: 'compressed',
            }))
              .pipe(postcss( [
                  cachebuster({
                    type: 'checksum',
                    cssPath : '/htdocs/css',
                  }),
                  ]))
                .pipe(gulpif(config.sourceMaps, sourcemaps.write()))
                  .pipe(gulp.dest('./htdocs/css/'));
    cb();
});

gulp.task('html', function (cb) {
    gulp.src('./src/**/*.html')
      .pipe(htmlrev())
        .pipe(gulp.dest('./htdocs'));
    cb();
});

gulp.task('watch', function(cb){
    // auto compile
    gulp.watch(
        ['./src/**/*.html',
         './src/javascripts/**/*.js',
         './src/scss/**/*.scss',
         ],
        gulp.series('html', 'javascript', 'sass'));
    
    // browser live reload
    gulp.watch('htdocs').on('change', function(filepath){
        gulp.src(filepath, { read: false }).pipe(connect.reload());
    });
    cb();
});

gulp.task('connect', function(done) {
  connect.server({
    root: 'htdocs',
    livereload: true
  }, function () { this.server.on('close', done) });
});

gulp.task('default', gulp.series(
    'html', 'javascript', 'sass',
    'watch', 'connect'));

しかし、これ重くならないかな?

*1:特にCompass使っててCompassライブラリCSSに依存してる時とか

gulp4を使ってCache bustingする方法(その1・環境構築編)

CSS/JavaScript/画像ファイルが更新されたらブラウザキャッシュの対策をしたいのです。

で、それらのMD5値をQueryStringに付ければいいんじゃない?と思ったので、それを自動でやる方法。…について調べてたら、色々とやりたくなった。

そのために、CSSJavaScript、画像が更新された際にQueryStringを付けて呼び出すようにする。

前提条件

  • CSSJavaScriptはライブラリも含めて1ファイルにしてしまう。
  • SCSSやTypeScriptのプリプロセッサは別物が動作しても良いようにする。もちろん、gulpでプリプロセスしてもO.K
  • 私が使っている環境はWindows10 / cygwin

gulpを使えるようにする。

Node.jsをインストール

適当なディレクトリを掘って移動

$ mkdir gulp_sample
$ cd gulp_sample

package.jsonを作る

$ npm init -y

gulpをpackage.jsonにも記載する形でインストール。

$ npm i -D gulp

gulpを実行

$ npx gulp

npxを通してgulp実行することでこのプロジェクト内でのnodeライブラリのバージョンで実行できる。npxはRailsのbundle execみたいな感じ。
npm 5.2.0の新機能! 「npx」でローカルパッケージを手軽に実行しよう - Qiita
が参考になった。

ファイル構成

こんな感じ

$ tree
.
├── gulpfile.js
├── htdocs
│   ├── css
│   ├── index.html
│   └── js
├── package.json
├── package-lock.json
└── src
    ├── index.html
    ├── javascripts
    │   ├── main.js
    │   └── vendor
    │       └── jquery-3.4.1.js
    └── scss
        ├── _base.scss
        ├── _footer.scss
        ├── _header.scss
        ├── _mixin.scss
        ├── _vars.scss
        ├── main.scss
        └── vendor
            ├── lity.scss
            ├── media-queries.scss
            ├── normalize.scss
            └── slick.scss

gulpfile.jsを作る

var gulp = require('gulp');

gulp.task('html', function (cb) {
  gulp.src('./src/*.html')
    .pipe(gulp.dest('./htdocs'));
  cb();
});
 
gulp.task('default', gulp.series('html'));

"html"というタスクを作り、そのタスクは、src/*htmlというファイルをhtdocsに出力するというだけのタスク。別にタスク名はhtmlでもhtml-compileでも構わない。

$ npx gulp

コンパイル?してくれる。

cb()の必要性

これが無いとエラーになる。
qiita.com

watchで自動gulp実行

gulp.task('html', function (cb) {
    gulp.src('./src/*.html')
      .pipe(gulp.dest('./htdocs'));
    cb();
});

gulp.task('watch', function(cb){
    gulp.watch(['./src/*.html'],
               gulp.parallel('html'));
    cb();
});

gulp.task('default', gulp.series('html', 'watch'));

taskに'watch'というのを加えて、これを実行するようにしている。
実行内容は、'./src/*.html' というファイルを監視して、それに変更があった場合にparallelでタスク"html"を実行する。タスク1個だけ指定なのでparallelの意味は無いけど。

ちなみに、最後の、「gulp.task('default', gulp.series('html', 'watch'));」に'html'を入れているのは最初の1回はファイル監視対象外になってしまうので1回は初期時に入れる必要がある。seriesは直列実行、parallelは並列実行。

ブラウザのlive reloadを利用したい

gulpでサーバーを立てて、ファイルを監視してファイルが変更あったら自動的にリロードするやつ。

gulp-connectをインストールしてgulpfile.jsを記述

$ npm i -D gulp-connect
// connectを使えるようにして
var connect = require('gulp-connect');
// タスクを登録
gulp.task('connect', function(done) {
  connect.server({
    root: 'htdocs',
    livereload: true
  }, function () { this.server.on('close', done) });
});
// initに加える
gulp.task('default', gulp.series('html', 'watch', 'connect'));

デフォルトポートは8080なので、http://localhost:8080/ でアクセスできるようになる。しかし、livereloadが効かない。

livereloadを効かす

なんか、Gulp4とgulp-connectの相性が悪いらしい。
github.com で、この方のコメントを参考に直してみる。

要は、srcディレクトリを監視して、リロードするというタスクを加える。

var gulp = require('gulp');
var connect = require('gulp-connect');

gulp.task('html', function (cb) {
    gulp.src('./src/*.html')
      .pipe(gulp.dest('./htdocs'));
    cb();
});

gulp.task('watch', function(cb){
    gulp.watch(['./src/*.html'],
               gulp.parallel('html'));
    cb();
});

gulp.task('connect', function(done) {
  connect.server({
    root: 'htdocs',
    livereload: true
  }, function () { this.server.on('close', done) });
 
  gulp.watch(['htdocs/*'], gulp.parallel('live-reload'));
});
gulp.task('live-reload', function(done){
    gulp.src('htdocs/*').pipe(connect.reload());
    done();
});

gulp.task('default', gulp.series('html', 'watch', 'connect', 'live-reload'));

なんか、 gulp.watch(['htdocs/'], gulp.parallel('live-reload'));とgulp.src('htdocs/')とで同じパスを指定しないといけないのが辛いなー。参照元の人は、filepathをonchangeで手に入れてるのが何とかならんかな…

ということで、ちょっと見直してこんな感じ。

var gulp = require('gulp');
var connect = require('gulp-connect');

gulp.task('html', function (cb) {
    gulp.src('./src/*.html')
      .pipe(gulp.dest('./htdocs'));
    cb();
});

gulp.task('watch', function(cb){
    gulp.watch(['./src/*.html'],
               gulp.parallel('html'));
    gulp.watch('htdocs').on('change', (filepath)=>{
        gulp.src(filepath, { read: false }).pipe(connect.reload());
    });
    cb();
});

gulp.task('connect', function(done) {
  connect.server({
    root: 'htdocs',
    livereload: true
  }, function () { this.server.on('close', done) });
});

gulp.task('default', gulp.series('html', 'watch', 'connect'));

いい感じになった。

JavaScriptの圧縮

JavaScriptをMinifyするgulp-uglifyと、複数ファイルをまとめるgulp-concat、JavaScript/SCSSの誤りでwatchが停止しないようgulp-plumber

$ npm i -D gulp-uglify gulp-plumber gulp-concat

gulpfile.jsに追加

var uglify      = require("gulp-uglify");
var plumber     = require("gulp-plumber");
var concat      = require("gulp-concat");
var sourcemaps  = require("gulp-sourcemaps");

gulp.task('javascript', function(cb){
    gulp.src(['./src/javascripts/vendor/**/*.js', './src/javascripts/*.js'])
      .pipe(plumber())
        .pipe(concat('dist.js'))
          .pipe(uglify())
            .pipe(gulp.dest("htdocs/js/"));
    cb();
});
// (略)
gulp.task('watch', function(cb){
    gulp.watch(['./src/**/*.html', './src/js/main.js'],
               gulp.parallel('html', 'javascript'));
    gulp.watch('htdocs').on('change', (filepath)=>{
        gulp.src(filepath, { read: false }).pipe(connect.reload());
    });
    cb();
});
// (略)
gulp.task('default', gulp.series('html', 'javascript', 'watch', 'connect'));

SCSSのコンパイルと圧縮

よく使われるやつ。

ソースマップを見たいのでgulp-sourcemapsを入れる

$ npm i -D  gulp-sourcemaps

更に、引数でsourcemapsを有効/無効にしたいので、minimistも入れる。あと、引数を元にifするのが意外とすんなりいかなかったのでこの記事を参考に、gulp-ifを使うようにした。gulp-modeもあったんだけど、廃止予定のgulp-utilを含んでいるので使わないようにした。 symfonycasts.com

$ npm i -D  minimist
$ npm i -D  gulp-if

gulpfile.jsに追加(下記はJavaScriptの所だけ)

var minimist    = require("minimist");
var gulpif      = require('gulp-if');

const options = minimist(process.argv.slice(2), {
  string: 'env',
  default: { env: 'production' }
});
const config = {
    production: options.env !== 'dev',
    sourceMaps: options.env === 'dev'
};

gulp.task('javascript', function(cb){
    gulp.src(['./src/javascripts/**/*.js'])
      .pipe(plumber())
        .pipe(gulpif(config.sourceMaps, sourcemaps.init()))
          .pipe(concat('dist.js'))
            .pipe(uglify())
              .pipe(gulpif(config.sourceMaps, sourcemaps.write()))
                .pipe(gulp.dest("htdocs/js/"));
    cb();
});

これで、以下の様にgulpを起動した時だけSourceMapが付く。

$ npx gulp --env dev

ということで、まとめ

package.js

{
  "name": "gulp2",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "gulp": "^4.0.2",
    "gulp-concat": "^2.6.1",
    "gulp-connect": "^5.7.0",
    "gulp-if": "^2.0.2",
    "gulp-plumber": "^1.2.1",
    "gulp-sass": "^4.0.2",
    "gulp-sourcemaps": "^2.6.5",
    "gulp-uglify": "^3.0.2",
    "minimist": "^1.2.0"
  }
}

gulpfile.js

var gulp        = require('gulp');
var connect     = require('gulp-connect');
var uglify      = require("gulp-uglify");
var plumber     = require("gulp-plumber");
var concat      = require("gulp-concat");
var sourcemaps  = require("gulp-sourcemaps");
var sass        = require('gulp-sass');
var minimist    = require("minimist");
var gulpif      = require('gulp-if');


const options = minimist(process.argv.slice(2), {
  string: 'env',
  default: { env: 'production' }
});
const config = {
    production: options.env !== 'dev',
    sourceMaps: options.env === 'dev'
};

gulp.task('javascript', function(cb){
    gulp.src(['./src/javascripts/**/*.js'])
      .pipe(plumber())
        .pipe(gulpif(config.sourceMaps, sourcemaps.init()))
          .pipe(concat('dist.js'))
            .pipe(uglify())
              .pipe(gulpif(config.sourceMaps, sourcemaps.write()))
                .pipe(gulp.dest("htdocs/js/"));
    cb();
});

gulp.task('sass', function (cb) {
    gulp.src(['./src/scss/**/*.scss'])
      .pipe(plumber())
        .pipe(gulpif(config.sourceMaps, sourcemaps.init()))
          .pipe(concat('style.css'))
            .pipe(sass())
              .pipe(gulpif(config.sourceMaps, sourcemaps.write()))
                .pipe(gulp.dest('./htdocs/css/'));
    cb();
});

gulp.task('html', function (cb) {
    gulp.src('./src/**/*.html')
      .pipe(gulp.dest('./htdocs'));
    cb();
});

gulp.task('watch', function(cb){
    gulp.watch(
        ['./src/**/*.html',
         './src/javascripts/**/*.js',
         './src/scss/**/*.scss',
         ],
        gulp.parallel('html', 'javascript', 'sass'));
    gulp.watch('htdocs').on('change', function(filepath){
        gulp.src(filepath, { read: false }).pipe(connect.reload());
    });
    cb();
});

gulp.task('connect', function(done) {
  connect.server({
    root: 'htdocs',
    livereload: true
  }, function () { this.server.on('close', done) });
});

gulp.task('default', gulp.series('html', 'javascript', 'sass', 'watch', 'connect'));

何か色々ととりあえずだけど。

aitendoのLEDのスペックの読み方

aitendoでLED買うときに、こんな感じで色とかのスペックが分からなくて非常に困る…

f:id:tohokuaiki:20190518133020p:plain
aitendoのLED選択

ということで、自分なりに解読メモ。

f:id:tohokuaiki:20190518132841p:plain
aitendoのLEDの読み方

頭の形
FL 平型
SH 弾頭型
頭直径
5 5mm
3 3mm
Nがある場合 頭のちょっと引っかかるところ(ハチマキ)が無い
発光色
R
B
Y 黄色
W
WW 暖色の白
G
O オレンジ
PK ピンク
PU
JG ジェードグリーン(ちょっと白みがかった緑)
レンズ色
WC 透明
D 曇りガラス
BWC 青色透明
短足
SP アノード17mm
(表記無し) アノード27mm

参考

平頭型LED(3mm/20個入) - aitendo
平頭型LED(3mm/20個入) - aitendo
平頭型LED(5mm/20個入) - aitendo
砲弾型LED(φ5mm/20個入) - aitendo
砲弾型短足LED(φ3mm/20個入) - aitendo
短足LED(20個入) - aitendo
砲弾型短足LED(φ3mm/20個入) - aitendo
短足LED(20個入) - aitendo