1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Astroで極力フレームワークっぽさを無くす

Posted at

いままで、HTMLの生成はpugを使用していたのですが、
このご時世、そろそろ他にいいものがあるのでは…?
と調べてみたところ、今更ながらAstroを知ったので、さっそく案件に取り入れてみた記録

極力生成されたソースにAstroで作ったという痕跡を残してほしくないということで、
デフォルトと異なる設定にした場所中心の備忘となります。

node -v 22.13.1
npm -v 10.9.2
astro@5.2.5

での内容です

要件

  1. APIなどのつなぎ込みのない、HMTLとCSSとJSで静的なモックを作成する
  2. 生成したHTMLを直接ブラウザで閲覧出来るようにする
  3. 相対パスでパス指定する
  4. IE11は非対応でOK

HTML関連

インデント有にしてほしい

とのことだったので、astro.configにてインデントありの設定に変更
compressHTML: false, (公式
結果、生成されたHTMLはインデントがついてはいる。が、結構ぐちゃぐちゃでした。
なので、astroでビルド→prettierで整形という処理をbuild時に実行するように修正。
prettierは最大80文字と、空白処理をコマンドで上書き
--print-width 1000 --html-whitespace-sensitivity ignore
(一応)先方の納得のいく形になりました。

改行コードはCRLFにしたい

Astroでbuildをしたソースは、LFになるようです。
ビルド後のソースの改行コード変更に関するconfigの設定は公式でなさそう…
CRLFに変更という処理を自前で設定(node convertLine.cjs)
こちらも、build時にastroでビルド→LFをCRLFに変更という処理を自前で追加しました

convertLine.cjs
convertLine.cjs

const fs = require('fs');
const path = require('path');

// 設定(対象フォルダ & 拡張子)
const targetDir = './dist'; // 変換したいフォルダのパス
const extensions = ['.html', '.css', '.js']; // 変換対象の拡張子リスト

// 再帰的にフォルダ内のファイルを処理
function convertLineEndings(dir) {
  fs.readdirSync(dir, { withFileTypes: true }).forEach((entry) => {
    const fullPath = path.join(dir, entry.name);
    if (entry.isDirectory()) {
      // サブフォルダがあれば再帰処理
      convertLineEndings(fullPath);
    } else if (extensions.includes(path.extname(entry.name))) {
      // 指定の拡張子に一致するファイルを変換
      const content = fs.readFileSync(fullPath, 'utf8');
      // 改行を CRLF に統一
      const convertedContent = content.replace(/\r?\n/g, '\r\n');
      fs.writeFileSync(fullPath, convertedContent, 'utf8');
    }
  });
}
// 実行
convertLineEndings(targetDir);

相対パス指定

公式では相対パス指定は対応していないそうです。
「Astro 相対パス」などでググったらでてきます。
以下のパッケージを使用させていただきました。
astro-relative-links

CSS関連

スコープ対応しないでほしい

…お客さんの要望なら聞くしかありません。
.astro内に<style>タグを記述するのがAstroでは一般的など思われます。
その場合、<style is:global>とすると、スコープ対応されないようです。
公式:グローバルスタイル

外部のCSS(node_modules)や、そのほか作成したCSSやSCSSなどは
スクリプトの先頭にimportで読み込みを追加すればOKです

ex
---
import '@/assets/style/style.scss'
---

dev開発時の<style is:global>

  • devで開発中はバッティングしていなかったのに、ビルドしたらCSSがバッティングしてしまう
  • 別ファイルでis:globalにしたはずなのに、Styleが当たらない

ということが度々ありました。
dev開発時はグローバル指定が反映されない?ようです。

別ファイルでCSSにしてほしい

こちらもデフォルトだと、ビルド後は.cssファイルが出来上がるのではなくhead内にstyleタグでの追加となるようです。
公式に設定の変更方法がありました:build.inlineStylesheets
ということで、上記を参考にastro.configに以下を追記しました。

export default defineConfig({
    build: {
        inlineStylesheets: 'never',
        assets: 'assets',
    }
})

上記設定をすると、head内CSSがdist/assets/XXXX.cssというディレクトリに生成されます。
ここで新たな問題が発生です

別ファイルにしたCSSを指定の場所に格納したい

dist/assets/XXXX.cssではなく、dist/assets/style/XXXX.cssに変更したい。
上記設定のbuild.assets: 'assets'assets/styleに変更すれば解決します。
が、画像も同様にassets => assets/styleとなってしまうのです。

なので、viteのbuild設定にて、assets配下のファイル格納先を変更するように追記しました。

astro.config.mjs
astro.config.mjs
import { defineConfig } from "astro/config";

export default defineConfig({
  build: {
    inlineStylesheets: 'never',
    assets: 'assets',
  },
  vite: {
    build: {
      rollupOptions: {
        output: {// ★★以下を追記★★
          assetFileNames: (assetInfo) => {
            let extType = assetInfo.names[0].split('.').at(-1);
            if (extType && /png|jpe?g|svg|gif|webp|ico/i.test(extType)) extType = 'img';
            if (extType && /css|scss/i.test(extType)) extType = 'css';
            return `assets/${extType}/[name][extname]`;
          },
        },
      },
    },
  },
});

別ファイルにしたCSSを1つにまとめてほしい

どんどん実装していくと、CSSが増え、生成されたファイルが長くなります。
すると、生成されたCSSが分割されるようになりました。
原因はたぶん、Astroが使用してるviteのデフォルトでCSSコード分割が有効になっているからっぽいです。
https://ja.vite.dev/config/build-options.html#build-csscodesplit
こちらを無効にし、かつ、インライン記述するMAX値も0としておきます。

export default defineConfig({
  vite: {
    build: {
      rollupOptions: {
      assetsInlineLimit: 0, ★
      cssCodeSplit: false, ★

JS関連

scriptの読み込みをtype="module"ではなくしたい

ビルドされたファイルのscript読み込みはtype="module"での読み込みとなるようです。
これが直接ブラウザでみるとCORSのエラーになるようでした。

つまり、公式のように
.astroに<script>~<script>でもNG。
sciptタグでの読み込みもNG。
ファイル内の先頭にimportしてもNG。
となると、publicに.jsを作って、そこを外部JSとして読み込むようにするしかない…
という結論になりました。(もしかしたら他にいい方法があったのかも。)

なので、AstroでのJSビルドは行わず、rollupで別途ビルドするように設定ファイルを作成
ローカル開発時はpublic/assets/js/script.jsが出来上がるように設定
ビルド時はdist/assets/js/script.jsが出来上がるように設定
としました

rollup.config.js
rollup.config.js
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import terser from '@rollup/plugin-terser';
import watcher from 'rollup-plugin-watcher';

const srcDir = './src/assets/js/';
const publicDir = './public/assets/js/';
const distDir = './dist/assets/js/';

const isProduction = process.env.NODE_ENV === 'prod';

const pluginOptsion = [
  resolve(),
  commonjs({
    sourceMap: false,
  }),

  isProduction &&
    terser({
      compress: {
        drop_console: true,
        drop_debugger: true,
      },
    }),
  !isProduction && watcher(['src/assets/**/**.js']),
];

const watchOption = isProduction
  ? false
  : {
      include: 'src/assets/**/**.js',
      chokidar: {
        usePolling: false,
      },
    };

const config = [
  {
    input: `${srcDir}AAA.js`,
    output: {
      file: isProduction ? `${distDir}mock.js` : `${publicDir}mock.js`,
      format: 'iife',
      name: 'AAA',
      sourcemap: !isProduction,
    },
    watch: watchOption,
    plugins: pluginOptsion,
  },
];
export default config;

asset関連

src配下に複数のディレクトリに分けて画像を格納し、astroのImageで読み込むと、
distの中に生成されるときは全て1階層になります

src/assets/images/common/icon.png => dist/assetes/images/icon.png

distでもディレクトリ構造を保ったままにしたい場合、
public内に画像を格納し、Imageでの読み込みではなく普通にimgタグとして読み込むようにすればディレクトリを保ったままbuildができます。
Imageを使う&&ディレクトリ構造を保つ。というのは実現が難しいためどちらかしかできません。
という内容で相談のうえ、今回はImageを使用し、distの中は1階層になるで譲歩いただきました。

最終config

astro.config.mjs
astro.config.mjs

import relativeLinks from 'astro-relative-links';
import { defineConfig } from 'astro/config';
import autoprefixer from 'autoprefixer';
import path, { dirname } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// https://astro.build/config
export default defineConfig({
  server: {
    open: true,
  },
  compressHTML: true,
  integrations: [relativeLinks()],
  build: {
    format: 'file',
    inlineStylesheets: 'never',
    assets: 'assets',
  },

  vite: {
    resolve: {
      alias: {
        '@/': `${path.resolve(__dirname, 'src')}/`,
      },
    },
    build: {
      assetsInlineLimit: 1,
      cssCodeSplit: false,
      rollupOptions: {
        output: {
          // entryFileNames: `assets/js/script.js`,
          manualChunks: undefined,
          assetFileNames: (assetInfo) => {
            let extType = assetInfo.names[0].split('.').at(-1);

            if (extType && /png|jpe?g|svg|gif|tiff|webp|ico/i.test(extType)) {
              extType = 'img';
            }
            if (extType && /css|scss/i.test(extType)) {
              extType = 'css';
            }

            return `assets/${extType}/[name][extname]`;
          },
        },
      },
    },
    css: {
      preprocessorOptions: {
        scss: {
          // see: https://vite.dev/config/shared-options.html#css-preprocessoroptions
          api: 'modern',
        },
      },
      postcss: {
        plugins: [autoprefixer()],
      },
    },
  },
});


感想

これからは、静的なHTMLの作成とか、ちょっとしたサイトの作成など
全然Astro使っていきたいなと思いました…!

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?