Cosense Toolbox
userscript 一覧テーマ作成使い方パーサーGitHub ↗
cosense-prettier
基盤#Prettier#整形#コード#module

cosense-prettier

コードフォーマッタの Prettier を Cosense 上で動かせるようにした部品です。自作スクリプトから呼び出せば、ページ内のコードをきれいに整形できます(対応言語は TypeScript / JSX / JavaScript)。シンタックスハイライトの suger-high-es と組み合わせると、より快適なコード体験を作れます。

デモ

デモ

インストール

設定ページの code:script.js に貼る
import { prettierLoader } from "/api/code/cosense-toolbox/cosense-prettier/module.js";
ソースコード全文を見る(JS)
// メモリ効率的なモジュールローダー
class ModuleManager {
  constructor() {
    this.loadedModules = new Map();
    this.moduleCache = new Map();
  }

  async loadModule(cacheName, cacheKey, url) {
    try {
      // すでにロード済みの場合は再利用
      if (this.loadedModules.has(cacheKey)) {
        console.log(`${cacheKey} already loaded, reusing...`);
        return this.loadedModules.get(cacheKey);
      }

      const cache = await caches.open(cacheName);
      let response = await cache.match(cacheKey);

      if (!response) {
        console.log(`${cacheKey} not found in cache. Fetching...`);
        response = await fetch(url);

        if (!response.ok) {
          throw new Error(`Failed to fetch ${cacheKey}: ${response.status}`);
        }

        await cache.put(cacheKey, response.clone());
        console.log(`${cacheKey} cached successfully`);
      } else {
        console.log(`${cacheKey} loaded from cache`);
      }

      const jsCode = await response.text();

      // 分離されたスコープでモジュールを実行
      const moduleResult = this.executeInIsolatedScope(jsCode, cacheKey);

      // 結果をキャッシュ
      this.loadedModules.set(cacheKey, moduleResult);
      console.log(`${cacheKey} executed successfully`);

      return moduleResult;
    } catch (error) {
      console.error(`Error loading ${cacheKey}:`, error);
      throw error;
    }
  }

  // 分離されたスコープでコードを実行
  executeInIsolatedScope(jsCode, moduleName) {
    // グローバル変数をキャプチャ
    const globalsBefore = Object.keys(window);

    try {
      // 一時的なスコープでコードを実行
      const moduleFunction = new Function(`
								const globalsBefore = ${JSON.stringify(globalsBefore)};
								return (function() {
										${jsCode}
										
										// モジュールが作成したグローバル変数を収集
										const moduleExports = {};
										const globalsAfter = Object.keys(window);
										
										for (const key of globalsAfter) {
												if (!globalsBefore.includes(key)) {
														moduleExports[key] = window[key];
												}
										}
										
										return moduleExports;
								})();
						`);

      const moduleExports = moduleFunction.call(null);

      // グローバル変数をモジュール参照に移動
      return {
        exports: moduleExports,
        cleanup: () => this.cleanupModule(moduleName, moduleExports),
      };
    } catch (error) {
      console.error(`Error executing module ${moduleName}:`, error);
      throw error;
    }
  }

  // モジュールのクリーンアップ
  cleanupModule(moduleName, moduleExports) {
    // グローバル変数を削除
    Object.keys(moduleExports).forEach((key) => {
      if (window[key] === moduleExports[key]) {
        delete window[key];
      }
    });

    // キャッシュから削除
    this.loadedModules.delete(moduleName);
    console.log(`${moduleName} cleaned up`);
  }

  // 全モジュールのクリーンアップ
  cleanupAll() {
    this.loadedModules.forEach((module, name) => {
      if (module.cleanup) {
        module.cleanup();
      }
    });
    this.loadedModules.clear();
    console.log("All modules cleaned up");
  }
}

// より効率的なPrettierローダー
class PrettierLoader {
  constructor() {
    this.moduleManager = new ModuleManager();
    this.prettier = null;
    this.plugins = null;
    this.isLoaded = false;
  }

  async load() {
    if (this.isLoaded) {
      return { prettier: this.prettier, plugins: this.plugins };
    }

    try {
      // Prettierメインをロード
      const prettierModule = await this.moduleManager.loadModule(
        "prettier-modules",
        "prettier-module",
        "https://scrapbox.io/files/6863b347ae01a710c9e24f8c.2",
      );

      // Babelパーサーをロード
      const babelModule = await this.moduleManager.loadModule(
        "prettier-modules",
        "prettier-parser-babel",
        "https://scrapbox.io/files/6863b3425750cde42c4b70f9.2",
      );

      // Estreeパーサーをロード
      const estreeModule = await this.moduleManager.loadModule(
        "prettier-modules",
        "prettier-estree",
        "https://scrapbox.io/files/6863b3443f96998565ad42ad.2",
      );

      // モジュールのエクスポートから必要なオブジェクトを取得
      this.prettier = prettierModule.exports.prettier || window.prettier;
      this.plugins = babelModule.exports.prettierPlugins || window.prettierPlugins;

      this.isLoaded = true;

      return { prettier: this.prettier, plugins: this.plugins };
    } catch (error) {
      console.error("Failed to load Prettier:", error);
      throw error;
    }
  }

  async format(code, options = {}) {
    if (!this.isLoaded) {
      await this.load();
    }

    const defaultOptions = {
      parser: "babel",
      plugins: this.plugins,
      semi: true,
      singleQuote: true,
      ...options,
    };

    return this.prettier.format(code, defaultOptions);
  }

  // Prettierのクリーンアップ
  cleanup() {
    this.moduleManager.cleanupAll();
    this.prettier = null;
    this.plugins = null;
    this.isLoaded = false;

    // 強制ガベージコレクション(開発環境でのみ)
    if (typeof window.gc === "function") {
      window.gc();
    }
  }
}

// 使用例
export const prettierLoader = new PrettierLoader();

// ページ離脱時にクリーンアップ
window.addEventListener("beforeunload", () => {
  prettierLoader.cleanup();
});

// 手動クリーンアップ用(開発時など)
window.cleanupPrettier = () => {
  prettierLoader.cleanup();
  console.log("Prettier manually cleaned up");
};

// メモリ使用量監視(開発用)
function monitorMemory() {
  if (performance.memory) {
    console.log("Memory usage:", {
      used: Math.round(performance.memory.usedJSHeapSize / 1024 / 1024) + " MB",
      total: Math.round(performance.memory.totalJSHeapSize / 1024 / 1024) + " MB",
      limit: Math.round(performance.memory.jsHeapSizeLimit / 1024 / 1024) + " MB",
    });
  }
}

// 定期的なメモリ監視(開発時のみ)
// setInterval(monitorMemory, 10000);
元のCosenseページを見る ↗

Cosense Toolbox / Cosense をもっと面白く、もっと強力に使うためのツール群

GitHub ↗

マイ・ツールボックス

    積んだ import を1つにまとめてコピー → 自分の設定ページ(ユーザー名ページ)に貼るだけ。