🚀

Astroでcssファイル名を無理矢理変更してみた

2023/05/02に公開

Astroでscssファイルを複数importしビルドした際に、ファイル名が全て同じになってしまい区別がつきませんでした。。
正直言ってマジでこれも需要があるかどうかわかんないのに、無理矢理変更できるよう組んでみました

css名をstyle-ほにゃららになるように設定しています
以下のように吐き出すよう設定しています

return `assets/[ext]/[name]-[hash][extname]`;

まずimportしてみる

例えばsample.scssとsample02.scssをそれぞれsample.css, sample02.cssのように出力したいと思っても、index.astroでimportすると...

index.astro
---
(中略)
import "src/scss/style.scss";
import "src/scss/sample.scss";
import "src/scss/sample02.scss";
---


全部index-ほにゃららになります...
style.css, sample.css, sample02.cssと出力されるのが理想なのですが、、、
これでは区別がつきません

import、もしくはindex.astroのstyleタグに記述があるとcssを吐き出すみたいです
scssをimport, index.astroとabout/index.astroのstyleタグに記述があるので3つ吐き出してます

ドキュメントも見ましたが、個別でリネームする手段がなさそうなので作ることとしました

手順

全部書くとbuild項目のところが長くなりすぎるので、npm i npm-run-allしてrun-sで書きます

sample.scssをsample.cssとして出力する例を書きます
index.astroでのimportは消しておいてください

吐き出し設定は以下のように[name]はやめ、style.scssのみimportさせます

return `assets/[ext]/style-[hash][extname]`;
index.astro
---
(中略)
import "src/scss/style.scss";
---

sassコンパイル

まずnpm i sassして、nodeで単純にコンパイルさせます
これでビルド後にsassコンパイルが走り、sample.cssが吐き出されます

package.json
{
(中略)
...
  "scripts": {
    "dev": "astro dev --host",
    "start": "astro dev",
    "build": "astro build",
    "postbuild": "run-s sass",
    "sass": "run-s sass:sample",
    "sass:sample": "sass src/scss/sample.scss dist/assets/css/sample.css --no-source-map",
  }
}

index.astroでの記述

普通にscss読み込ませます。

index.astro
<link rel="stylesheet" href="src/scss/sample.scss" />

これでdevは読んでくれるので問題ないです。
ただしビルドした後はhtmlファイルなのでscssが読めません
なのでhrefを変更させる必要があります

html書き換え用のjsを作成

まずastro.config.mjsと同階層にjsファイルを作成します

scssToCss.js
import fs from "fs";
import path from "path";

/**
 * ランダムな値を生成
 * @param {number} [digit=8] 桁数
 * @return {string} 生成されたランダムな桁数分の値を返す
 */
const randomStr = function (digit) {
  var patterns = '0123456789abcdefghijklmnopqrstuvwxyz'.split(''),
    val = '';

  if (typeof digit === 'undefined') digit = 8;

  for (var i = 0; i < digit; i++) {
    val += patterns[Math.floor(Math.random() * patterns.length)] + '';
  }
  return val;
};

//デフォルト(import)で読み込むscss
const defaultImportScss = "style.scss";

// src/scss直下のscssファイル一覧取得
const scssFilesList = fs.readdirSync("src/scss").filter((file) => {
  if (file.indexOf(".scss") !== -1) return file;
});

// src/scss直下のデフォルトで読み込むscss以外のscssファイル一覧
let scssFilesListExceptStyle = scssFilesList.filter((file) => {
  if (file.indexOf(defaultImportScss) === -1) return file;
});

//index.astro内のlinkタグで読み込んだscssのhrefを取得
let srcScssHref = [];
scssFilesListExceptStyle.forEach((fileName) => {
  srcScssHref.push(`src/scss/${fileName}`);
});

let indexHtmlList = ""; //.index.html配列

/**
 * htmlファイル一覧を配列に格納し変数に代入する関数
 */
function getIndexHtmlList() {
  const dirTarget = "dist";

  //即時関数getFiles(ファイル情報取得)
  (function getFiles(dir) {
    var files = fs.readdirSync(dir); // 指定フォルダ内のファイル、サブフォルダを取得
    files.forEach(function (file) {
      var fullPath = path.join(dir, file); // フルパスを取得
      // log(fullPath)
      var stats = fs.statSync(fullPath); // ファイル(またはフォルダ)の情報を取得
      if (stats.isDirectory()) {
        // フォルダの場合
        getFiles(fullPath); // getFilesを再帰的に呼び出し
      }

      if (fullPath.indexOf(".html") !== -1) {
        indexHtmlList += fullPath + ",";
      }
    });
  })(dirTarget);

  //配列に直し、空要素の削除
  indexHtmlList = indexHtmlList.split(",").filter(Boolean);
}
getIndexHtmlList();

/**
 * htmlファイル書き換え関数
 */
const rewriteHtml = () => {
  indexHtmlList.forEach((index) => {
    // *.htmlファイルの読み込み
    const indexDocument = fs.readFileSync(index, "utf-8");

    //htmlファイル内のlinkタグのhrefに配列srcScssHrefにある値(src/scss/*.scss)が含まれていた場合
    srcScssHref.forEach((scss, i) => {
      if (indexDocument.indexOf(scss) !== -1) {
        //ハッシュ生成
        const hash = randomStr();
        //ファイルパス名を.cssに変換
        const cssName = scssFilesListExceptStyle[i].replace("scss", "css");
        //ハッシュを加えたcssファイル名を生成
        const cssNameHash = `${cssName.split(".")[0]}-${hash}.css`;
        //hrefをscss用からcss用に修正
        const result = indexDocument.replace(scss, `./assets/css/${cssNameHash}`);

        //html書き込み
        fs.writeFileSync(index, result);

        //cssファイル名をハッシュ付きに変更
        fs.renameSync(
          path.join(`./dist/assets/css/`, cssName),
          path.join(`./dist/assets/css/`, cssNameHash)
        );
      }
    });
  });
};
rewriteHtml();

const defaultImportScss = "style.scss";
これはimportするscssファイルを指定することで

let scssFilesListExceptStyle = scssFilesList.filter((file) => { if (file.indexOf(defaultImportScss) === -1) return file; });
この時、その指定されたscssファイル以外の配列を作ってます
この例で言えば、scssFilesListExceptStyle = ["sample.scss"]になるってことです

で、その配列をもとにdist内のhtmlを書き換える作業に入ります
全部説明するのはしんどいので省略します

Astroがデフォルトで吐き出すcssにハッシュが付くようにしているので、sample.cssにもハッシュ(正確に言えばただの文字列)を加えるようにしてあります
ただしlinkタグでscss読み込みしていない場合はハッシュがつきません

scriptsにjs実行を登録

先ほどのjsを作って、ビルド後に実行する必要があります
package.jsonを以下のように修正します

package.json
{
  (中略)
  ...
  "scripts": {
    "dev": "astro dev --host",
    "start": "astro dev",
    "build": "astro build",
-   "postbuild": "run-s sass",
+   "postbuild": "run-s sass && run-s scssToCss",
    "sass": "run-s sass:sample",
    "sass:sample": "sass src/scss/sample.scss dist/assets/css/sample.css --no-source-map",
+   "scssToCss": "node scssToCss.js",
  }
}

そうするとこんな感じでハッシュがつきます

hoge.scssもhoge.cssとして出力したいみたいな場合は以下のようにscriptsを追加する必要があります

"sass": "run-s sass:sample && run-s sass:hoge",
"sass:sample": "sass src/scss/sample.scss dist/assets/css/sample.css --no-source-map",
"sass:hoge": "sass src/scss/hoge.scss dist/assets/css/hoge.css --no-source-map",

Discussion