Dart Sassを使う準備&gulp(備忘録)

2022年10月でLibSass(node-sass)のサポートが終了なので
Lib Sass(node-sass)からDart Sass(sass)へ切り替えをします

@use@forwardでのフォルダ構成とgulpで普段使えるような設定ファイルを書いてみようと思います

ちなみに

webpackでnode-sassをインストールしている場合は
「node-sass」をアンインストールして「dart-sass」をインストールすればいいだけのようです
(まだ試していません💦)
dart-sassのパッケージ名はシンプルに「sass」です

npm install sass
目次
  1. @useと@forward
  2. フォルダとファイルの構成
  3. gulpでDart Sassをトランスパイル
  4. gulpの基本
  5. gulpの設定ファイル
    1. SCSSのトランスパイル
    2. 画像の圧縮
    3. JavaScriptのバンドル
    4. watchとbrowsersync
    5. タスクをまとめて実行&フォルダ削除
    6. 圧縮フォルダを作成
    7. package.jsonの編集

@useと@forward

@useの場合はファイルの内容は継承されません

@forwardでファイルを読み込むと、@forwardで読み込んでいるファイルの内容を引き継ぐことができます
*変数やmixinのファイルをまとめて@forwardで読み込んだ代表ファイルを作成
変数やmixinを使うファイルは代表ファイルを@useで読み込みます

@forward "variables";
@forward "mixin";

@use
変数やmixinを使う時は名前空間を指定します

//名前空間を設定しない
@use "../global" as *;
//使用する
変数名

//ファイル名が名前空間
@use "../global";
//使用する
global.変数名

//名前空間を指定する場合
@use "../global" as g;
//使用する
g.変数名

フォルダとファイルの構成

*注意:思いつきだけで試してもいません💦

sass
|_global (他のファイルから参照する変数やmixin)
    |_ _index.scss(代表ファイル)
    |_ _variables.scss
    |_ _mixin.scss
    |_...
|_ common(他のファイルで@extendで使う)
    |_ _index.scss(代表ファイル)
    |_ _fontsize.scss
    |_ _spacing.scss
    |_ _wrapper.scss
|_ page(レイアウトなのでcommonとglobalを読みこむイメージ)
    |_ _home.scss
    |_...
|_component(globalを読みこむイメージ)
    |_button.scss
    |_...
|_setting
    |_reset.scss
    |_base.scss(imgなどタグにつけるcss)
    |_...
|_bundle.scss (globalとcommon以外のファイルを@useで読み込む)

bundle.scss
*globalとcommon以外のファイルを@useで読み込む

@use "page/home";
@use "component/button";
.
.
.

page配下のファイル
*基本的にレイアウトなのでcommonとglobalを読みこむイメージ

@use "../global" as *;
@use "../common" as *;

_mixin.scssの例
ブレイクポイントでメディアクエリを作成

$breakpoints: (
  // 600px以上
  'md': 'screen and (min-width: 600px)',
  // 1024px以上(iPadminiの横幅でiPadProの縦幅)
  //個人的には2カラム(サイドバーありの場合)のカラム落ちのポイントです
  'lg': 'screen and (min-width: 1024px)',
) !default;

@mixin mq($breakpoint: md) {
  @media #{map-get($breakpoints, $breakpoint)} {
    @content;
  }
}

//使用するとき
@use "../global" as *;
 @include mq(md){

}
 @include mq(lg){

}

_fontsize.scssの例

@use "../globals" as *; //メディアクエリを使うため
.sm {
  font-size: 0.75rem;
  @include mq(lg) {
    font-size: 1rem;
  }
}
.md {
  font-size: 1.25rem;
  @include mq(lg) {
    font-size: 1.5rem;
  }
}
.lg {
  font-size: 1.5rem;
  @include mq(lg) {
    font-size: 2rem;
  }
}
.main-title {
    @extend .lg;
    font-weight: 600;
}

//可変にしてしまう場合(メディアクエリ不要)
.sm {
 font-size: clamp(0.75rem, 0.659rem + 0.45vw, 1rem);
}

_wrapper.scssの例

@use "../global" as *; //変数$maxWidthやメディアクエリを使うため
.wrapper {
  width: 90%;
  margin: 0 auto;
  max-width: $maxWidth;
}
//最大幅を設定しない
.wrapper{
  padding: 0 5vw;
}
//カラム
.flex {
  display: flex;
  flex-direction: column;
  @include mq(md) {
    flex-direction: row;
    flex-wrap: wrap;
  }
}

gulpでDart Sassをトランスパイル

2パターンあるようです

  • gulp-dart-sass(Dart Sassをトランスパイルする)
  • sass(Dart Sassを使えるようにする)とgulp-sass(sassをトランスパイルする)

余談:調べているとよく目にするので
gulp-sass-glob-use-forward
glob機能を使って@useや@forwardを省略するパッケージ
@use "./page/*";
上記の用にフォルダを書くだけ
*個別のファイル名は書かなくてもOK

そもそもトランスパイルだけならgulpを使わなくても「npm scripts」でOK
packgage.jsonの”scripts”に追加
npm run sassを実行
scssフォルダをトランスパイルしてcssフォルダに書き出し変更を監視します

npm install sass
{
  "scripts": {
    "sass": "sass --watch scss:css"
  }
}

gulpの基本

設定ファイル

gulpfile.js

タスク単位で命令を管理実行します

name:実行するタスク名
gulp タスク名でタスクを実行します
'default'にすると gulpでタスクを実行します
deps:Array タスクを実行する前に完了しておきたい別のタスク名
fn:実行するタスク内容

gulp.task(name[, deps], fn)
//gulp.task()がgulp4で非推奨になり
gulp.task('task', () => {
  ...
})
//関数宣言とexportsが推奨
function task() { 
  ...
}
exports.task = task; // `gulp task`で実行可能

srcで対象のファイルを指定し、dest出力先のフォルダを指定
個々の処理はpipeでつなぎます

入力先
globs: Arrayも可ー 読み込みたいファイルのパス名のパターン文字列(配列)
options:{
buffer:true(バッファファイル)
base: gulp.dest()に指定するディレクトリの構成
}

gulp.src(globs[, options])

出力先
options:{
cwd: 基準
mode: パーミッションを指定
}
デフォルト値は{cwd: process.cwd(), mode: ‘0777’}

gulp.dest(path[, options])

入出力先が同じタスクを、pipeでつないで複数の処理を実行します

gulp.series :指定したものを順番に実行
gulp.parallel:指定したものを同時に実行

ファイルの変更を監視するgulp.watch()メソッドを使います

gulp.watch(glob [, opts], tasks)

gulpの設定ファイル

src/assets/をdist/assets/に出力します

プロジェクトフォルダ
|_src 
   |_assets
    |_ js
       |_ bundle.js(エントリーポイント)
    |_ scss
       |_ bundle.scss(エントリーポイント)
    |_ images

したいこと

  • Sassのトランスパイルとベンダープレフィックスを付与
  • sourcemapを作成
  • 画像の圧縮
  • JavaScriptのバンドル
  • 本番はCSSとJavaScriptコードの圧縮
  • ファイルを保存したら自動的でリロード
  • 必要なファイルをまとめた圧縮フォルダを作成

Babelなどのトランスパイルが必要な言語を使用して設定ファイルを作成する場合
*あくまでCommonJS
設定ファイルはgulpfile.babel.jsにして、.babelrcファイルを作成
https://github.com/gulpjs/gulp#use-latest-javascript-version-in-your-gulpfile

gulp-cliはグローバルにインストール済み

npm install --save-dev gulp
#babel
npm install --save-dev @babel/register @babel/core @babel/preset-env 
{
  "presets": [ "@babel/preset-env" ]
}

SCSSのトランスパイル

#Dart Sassをトランスパイル
npm install gulp-dart-sass --save-dev 
#CSSの圧縮(Minify)
npm install gulp-clean-css --save-dev
#gulpの処理中にifが使える
npm install gulp-if --save-dev 
#ソースマップを作成する(gulp-dart-sassもOK)
npm install gulp-sourcemaps --save-dev 
#ベンダープレフィックスを付与
npm install gulp-autoprefixer --save-dev 

備考

src/assets/scss/bundle.scssをトランスパイルしてdist/assets/cssに出力
gulp styleでトランスパイル
gulp style --prodでトランスパイルしたファイルは圧縮

import gulp from "gulp";
import sass from "gulp-dart-sass";
import cleanCSS from "gulp-clean-css";
import gulpif from "gulp-if";
import sourcemaps from "gulp-sourcemaps";
import autoprefixer from "gulp-autoprefixer";

const PRODUCTION = process.argv[3] !== undefined;

export const style = () => {
  return gulp
    .src("src/assets/scss/bundle.scss")
    .pipe(gulpif(!PRODUCTION, sourcemaps.init()))
    .pipe(sass().on("error", sass.logError))
    .pipe(autoprefixer())
    .pipe(gulpif(PRODUCTION, cleanCSS()))
    .pipe(gulpif(!PRODUCTION, sourcemaps.write()))
    .pipe(gulp.dest("dist/assets/css"))
};

画像の圧縮

gulp-imageminはバージョン8だとエラーになるのでバージョンを指定します

#画像の圧縮
npm install --save-dev gulp-imagemin@^7.1.0
import imagemin from "gulp-imagemin";
export const image = () => {
  return gulp
    .src("src/assets/images/**/*.{jpg,jpeg,png,svg,gif}")
    .pipe(gulpif(PRODUCTION, imagemin()))
    .pipe(gulp.dest("dist/assets/images"));
};

JavaScriptのバンドル

npm install --save-dev webpack-stream
# ローダー
npm install --save-dev babel-loader
#エントリーポイントが2つ以上の場合のファイル名のため
npm install --save-dev vinyl-named

webpack-stream
https://github.com/shama/webpack-stream

export const script = () => {
  return gulp
    .src(["src/assets/js/bundle.js"])
    .pipe(named())
    .pipe(
      webpack({
        module: {
          rules: [
            {
              test: /\.js$/,
              use: {
                loader: "babel-loader",
                options: {
                  presets: ["@babel/preset-env"],
                },
              },
            },
          ],
        },
        output: {
          filename: "[name].js",
        },
        devtool: !PRODUCTION ? "inline-source-map" : false,
        mode: PRODUCTION ? "production" : "development",
      })
    )
    .pipe(gulp.dest("dist/assets/js"));
};

watchとbrowsersync

gulpのwatchメソッドで修正の対象ファイルを監視
browsersyncでファイルに変更があればブラウザをリロード

npm install --save-dev browser-sync

Browsersync + gulp.js
https://browsersync.io/docs/gulp

import browserSync from "browser-sync";
const server = browserSync.create();

export const style = () => {
  return gulp
    //省略
    .pipe(gulp.dest("dist/assets/css"))
   //追加
    .pipe(server.stream());
};

export const serve = (done) => {
  server.init({
    baseDir: "./", //index.htmlをrootにおいている場合
  });
//ローカルサーバーの例
//   server.init({
//     proxy: "http://localhost/wordpress/",
//   });
  done();
};
export const reload = (done) => {
  server.reload();
  done();
};

export const watch = () => {
  gulp.watch("src/assets/scss/**/*.scss", style);
  gulp.watch("src/assets/js/**/*.js", gulp.series(script, reload));
  gulp.watch("src/assets/images/**/*.{jpg,jpeg,png,svg,gif}", gulp.series(image, reload));
//  gulp.watch("**/*.php", reload);
  gulp.watch("**/*.html", reload);
};

scssはリロードではなくストリームを使った方がいい
scssを少し編集するたびにリロードすると不便なのでstreamにして修正部分だけが反映させます

タスクをまとめて実行&フォルダ削除

distフォルダを削除するタスク
エラーになるのでバージョンを指定します

#グロブを使用してファイルとディレクトリを削除する
npm install --save-dev del@^6.1.1
import del from "del";
export const clean = () => del(["dist"]);

gulp build :本番時のタスクをまとめる
gulp dev:開発時のタスクをまとめる

export const dev = gulp.series(clean, gulp.parallel(style, script, image), serve, watch);
export const build = gulp.series(clean, gulp.parallel(style, script, image));

圧縮フォルダを作成

必要なファイルまとめた圧縮フォルダを作成

npm install --save-dev gulp-zip
import zip from "gulp-zip";
import info from "./package.json";
export const compress = () => {
  return gulp
    .src([
      "**/*",
      "!.vscode",
      "!node_modules{,/**}",
      "!packaged{,/**}",
      "!src{,/**}",
      "!.babelrc",
      "!.gitignore",
      "!gulpfile.babel.js",
      "!package.json",
      "!package-lock.json",
    ])
    .pipe(zip(`${info.name}.zip`))
    .pipe(gulp.dest("packaged"));
};

export const bundle = gulp.series(build, compress);

package.jsonの編集

npmコマンド(npm start ・npm run build・npm run bundle)で実行できるようにする

  "scripts": {
    "start": "gulp dev",
    "build": "gulp build --prod",
    "bundle": "gulp bundle --prod"
  },
省略
browserslisを追加
"browserslist": [
   "> 0.2%, not dead",
   "not IE 11"
 ]

browserslist
https://github.com/browserslist/browserslist

nodeバージョン:v16.15.1
*監視対象ファイルはphpでローカルサーバーを起動させています

コードを見る

{
  "name": "hoge",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "gulp dev",
    "build": "gulp build --prod",
    "bundle": "gulp bundle --prod"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.20.2",
    "@babel/preset-env": "^7.20.2",
    "@babel/register": "^7.18.9",
    "babel-loader": "^9.1.0",
    "browser-sync": "^2.27.10",
    "del": "^6.1.1",
    "gulp": "^4.0.2",
    "gulp-autoprefixer": "^8.0.0",
    "gulp-clean-css": "^4.3.0",
    "gulp-dart-sass": "^1.0.2",
    "gulp-if": "^3.0.0",
    "gulp-imagemin": "^7.1.0",
    "gulp-sourcemaps": "^3.0.0",
    "gulp-zip": "^5.1.0",
    "vinyl-named": "^1.1.0",
    "webpack-stream": "^7.0.0",
  },
  "browserslist": [
    "> 0.2%, not dead",
    "not IE 11"
  ]
}
import gulp from "gulp";
import sass from "gulp-dart-sass";
import cleanCSS from "gulp-clean-css";
import gulpif from "gulp-if";
import sourcemaps from "gulp-sourcemaps";
import autoprefixer from "gulp-autoprefixer";
import imagemin from "gulp-imagemin";
import webpack from "webpack-stream";
import named from "vinyl-named";
import browserSync from "browser-sync";
import del from "del";
import zip from "gulp-zip";
import info from "./package.json";

const PRODUCTION = process.argv[3] !== undefined;
const server = browserSync.create();

export const style = () => {
  return gulp
    .src("src/assets/scss/bundle.scss")
    .pipe(gulpif(!PRODUCTION, sourcemaps.init()))
    .pipe(sass().on("error", sass.logError))
    .pipe(autoprefixer())
    .pipe(gulpif(PRODUCTION, cleanCSS()))
    .pipe(gulpif(!PRODUCTION, sourcemaps.write()))
    .pipe(gulp.dest("dist/assets/css"))
    .pipe(server.stream());
};

export const image = () => {
  return gulp
    .src("src/assets/images/**/*.{jpg,jpeg,png,svg,gif}")
    .pipe(gulpif(PRODUCTION, imagemin()))
    .pipe(gulp.dest("dist/assets/images"));
};

export const script = () => {
  return gulp
    .src(["src/assets/js/bundle.js"])
    .pipe(named())
    .pipe(
      webpack({
        module: {
          rules: [
            {
              test: /\.js$/,
              use: {
                loader: "babel-loader",
                options: {
                  presets: ["@babel/preset-env"],
                },
              },
            },
          ],
        },
        output: {
          filename: "[name].js",
        },
        devtool: !PRODUCTION ? "inline-source-map" : false,
        mode: PRODUCTION ? "production" : "development",
      })
    )
    .pipe(gulp.dest("dist/assets/js"));
};
export const serve = (done) => {
  server.init({
    proxy: "http://localhost/wordpress/",
  });
  done();
};
export const reload = (done) => {
  server.reload();
  done();
};
export const watch = () => {
  gulp.watch("src/assets/scss/**/*.scss", style);
  gulp.watch("src/assets/js/**/*.js", gulp.series(script, reload));
  gulp.watch(
    "src/assets/images/**/*.{jpg,jpeg,png,svg,gif}",
    gulp.series(image, reload)
  );
  gulp.watch("**/*.php", reload);
};
export const compress = () => {
  return gulp
    .src([
      "**/*",
      "!.vscode",
      "!node_modules{,/**}",
      "!packaged{,/**}",
      "!src{,/**}",
      "!.babelrc",
      "!.gitignore",
      "!gulpfile.babel.js",
      "!package.json",
      "!package-lock.json",
    ])
    .pipe(zip(`${info.name}.zip`))
    .pipe(gulp.dest("packaged"));
};

export const clean = () => del(["dist"]);
export const dev = gulp.series(clean, gulp.parallel(style, script, image), serve, watch);
export const build = gulp.series(clean, gulp.parallel(style, script, image));
export const bundle = gulp.series(build, compress);
{
  "presets": [ "@babel/preset-env" ]
}

*webpackよりだいぶ楽だった気がします
webpack4で同じような機能の設定ファイルはあるのですが、5を触るのもややこしそうだし…
スピード云々…なんやかや…わたしには何がいいのかよくわかりません😂