「労働者が自分の仕事をうまくやりたいなら、まず自分の道具を研ぎ澄まさなければなりません。」 - 孔子、「論語。陸霊公」
表紙 > プログラミング > モジュールバンドラーを書きました。メモなど

モジュールバンドラーを書きました。メモなど

2024 年 8 月 5 日に公開
ブラウズ:381

I wrote a module bundler. notes, etc

シンプルな JavaScript バンドラーを構築しましたが、予想よりもはるかに簡単であることがわかりました。私が学んだすべてをこの投稿で共有します。

大規模なアプリケーションを作成する場合、JavaScript ソース コードを個別の js ファイルに分割することをお勧めします。ただし、複数のスクリプト タグを使用してこれらのファイルを HTML ドキュメントに追加すると、

などの新たな問題が発生します。
  • グローバル名前空間の汚染。

  • 競合状態。

モジュール バンドラーは、さまざまなファイルのソース コードを 1 つの大きなファイルに結合し、欠点を回避しながら抽象化の利点を享受できるようにします。

モジュール バンドラーは通常、これを 2 つのステップで実行します。

  1. エントリ ファイルから始まるすべての JavaScript ソース ファイルを検索します。これは依存関係の解決として知られており、生成されたマップは依存関係グラフと呼ばれます。
  2. 依存関係グラフを使用してバンドルを生成します。バンドルとは、ブラウザーで実行できる JavaScript ソース コードの大きな文字列です。これはファイルに書き込まれ、script タグを使用して HTML ドキュメントに追加されます。

依存関係の解決

前述したように、ここでは

  • エントリ ファイルを取得します、
  • コンテンツを読み取って解析します。
  • モジュールの配列に追加します
  • すべての依存関係 (インポートする他のファイル) を検索します、
  • 依存関係の内容を読み取り、解析します
  • 配列に依存関係を追加
  • 依存関係の依存関係などを検索し、最後のモジュールに到達するまで続けます

これを行う方法は次のとおりです (JavaScript コードが先にあります)

テキスト エディタでbundler.js ファイルを作成し、次のコードを追加します:

const bundler = (entry)=>{
          const graph = createDependencyGraph(entry)

          const bundle = createBundle(graph)
          return bundle
}

バンドラー関数はバンドラーの主要なエントリです。ファイル (エントリ ファイル) へのパスを取得し、文字列 (バンドル) を返します。その中で、createDependencyGraph 関数を使用して依存関係グラフを生成します。

const createDependencyGraph = (path)=>{
          const entryModule = createModule(path)

          /* other code */
}

createDependencyGraph 関数はエントリ ファイルへのパスを取得します。 createModule 関数を使用して、このファイルのモジュール表現を生成します。

let ID = 0
const createModule = (filename)=>{
          const content = fs.readFileSync(filename)
          const ast = babylon.parse(content, {sourceType: “module”})

          const {code} = babel.transformFromAst(ast, null, {
              presets: ['env']
            })

           const dependencies = [ ]
           const id = ID  
           traverse(ast, {
                   ImportDeclaration: ({node})=>{
                       dependencies.push(node.source.value)
                   }
            }
            return {
                           id,
                           filename,
                           code,
                           dependencies
                       }
}

createAsset 関数は、ファイルへのパスを取得し、その内容を文字列に読み取ります。この文字列は解析されて抽象構文ツリーになります。抽象構文ツリーは、ソース コードの内容をツリーで表現したものです。これは、HTML ドキュメントの DOM ツリーにたとえることができます。これにより、検索などのコード上でのいくつかの機能の実行が容易になります。
babylon パーサーを使用してモジュールから ast を作成します。

次に、babel コア トランスパイラーの助けを借りて、ブラウザ間の互換性のためにコード コンテンツを es2015 より前の構文に変換します。
その後、babel の特別な関数を使用して ast が走査され、ソース ファイル (依存関係) の各インポート宣言が検索されます。

次に、これらの依存関係 (相対ファイル パスの文字列テキスト) を依存関係配列にプッシュします。

また、このモジュールを一意に識別するための ID を作成します。
最後に、このモジュールを表すオブジェクトを返します。このモジュールには、ID、文字列形式のファイルの内容、依存関係の配列、および絶対ファイル パスが含まれています。

const createDependencyGraph = (path)=>{
          const entryModule = createModule(path)

          const graph = [ entryModule ]
          for ( const module of graph) {
                  module.mapping = { }
module.dependencies.forEach((dep)=>{
         let absolutePath = path.join(dirname, dep);
         let child = graph.find(mod=> mod.filename == dep)
         if(!child){
               child = createModule(dep)
               graph.push(child)
         }
         module.mapping[dep] = child.id
})
          }
          return graph
}

createDependencyGraph 関数に戻り、グラフを生成するプロセスを開始できます。私たちのグラフはオブジェクトの配列であり、各オブジェクトはアプリケーションで使用される各ソース ファイルを表します。
エントリーモジュールを使用してグラフを初期化し、それをループします。これには項目が 1 つしか含まれていませんが、エントリ モジュール (および追加する他のモジュール) の依存関係配列にアクセスして、配列の末尾に項目を追加します。

依存関係配列には、モジュールのすべての依存関係の相対ファイル パスが含まれます。配列はループされ、相対ファイル パスごとに絶対パスが最初に解決され、新しいモジュールの作成に使用されます。この子モジュールはグラフの最後にプッシュされ、すべての依存関係がモジュールに変換されるまでプロセスが最初からやり直します。
また、各モジュールは、各依存関係の相対パスを子モジュールの ID に単純にマップするマッピング オブジェクトを与えています。
モジュールの重複や無限循環依存関係を防ぐために、モジュールが既に存在するかどうかのチェックが各依存関係に対して実行されます。
最後に、アプリケーションのすべてのモジュールを含むグラフを返します。

バンドル

依存関係グラフが完了したら、バンドルを生成するには 2 つのステップが必要になります

  1. 各モジュールを関数でラップします。これにより、各モジュールが独自のスコープを持つという考えが生まれます
  2. モジュールをランタイムにラップします。

各モジュールをラッピングする

モジュールオブジェクトをbundle.jsファイルに書き込めるように文字列に変換する必要があります。これを行うには、moduleString を空の文字列として初期化します。次に、グラフをループして、各モジュールをキーと値のペアとしてモジュール文字列に追加します。モジュールの ID がキーであり、配列には 2 つの項目が含まれています。まず、関数でラップされたモジュールのコンテンツ (前述したようにスコープを与えるため) )、2 番目はその依存関係のマッピングを含むオブジェクトです。

const wrapModules = (graph)=>{
         let modules = ‘’
           graph.forEach(mod => {
    modules  = `${http://mod.id}: [
      function (require, module, exports) {
        ${mod.code}
      },
      ${JSON.stringify(mod.mapping)},
    ],`;
  });
return modules
}

また、各モジュールをラップする関数は、引数として require、export、および module オブジェクトを取ります。これは、これらがブラウザーには存在しないためですが、コード内に表示されるため、それらを作成してこれらのモジュールに渡します。

ランタイムの作成

これはバンドルがロードされるとすぐに実行されるコードで、モジュールに require、module、および module.exports オブジェクトを提供します。

const bundle = (graph)=>{
        let modules = wrapModules(graph)
        const result = `
    (function(modules) {
      function require(id) {
        const [fn, mapping] = modules[id];

        function localRequire(name) {
          return require(mapping[name]);
        }

        const module = { exports : {} };

        fn(localRequire, module, module.exports);

        return module.exports;
      }

      require(0);
    })({${modules}})`;
  return result;
}

モジュール オブジェクトを引数として受け取る、すぐに呼び出される関数式を使用します。その中で、その ID を使用してモジュール オブジェクトからモジュールを取得する require 関数を定義します。
ファイル パス文字列を ID にマップする特定のモジュールに固有の localRequire 関数を構築します。空のエクスポート プロパティ
を持つモジュール オブジェクト これはモジュールコードを実行し、localrequire、module、exports オブジェクトを引数として渡し、node js モジュールと同じように module.exports を返します。 最後に、エントリ モジュール (インデックス 0) で require を呼び出します。

バンドラーをテストするには、bundler.js ファイルの作業ディレクトリに、index.js ファイルと 2 つのディレクトリ (src ディレクトリとパブリック ディレクトリ) を作成します。

パブリック ディレクトリに、index.html ファイルを作成し、body タグに次のコードを追加します。


モジュール バンドラー
リリースステートメント この記事は次の場所に転載されています: https://dev.to/frontendokeke/i-wrote-a-module-bundler-notes-etc-4ofa?1 侵害がある場合は、[email protected] に連絡して削除してください。
最新のチュートリアル もっと>

免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。

Copyright© 2022 湘ICP备2022001581号-3