css または js ファイルのタイムスタンプ (またはハッシュ) 要求パラメーターを NextJS 12 ビルド file.js に追加する方法?ts=[timestamp]

NextJs アプリでビルドした後、次の形式の js および css ファイル名を使用する機会が必要です。
./path/file.js?ts=[タイムスタンプ]
./path/file.css?ts=[タイムスタンプ]

次の解決策を見つけました:

  • _document.tsx を作成し、Document から継承するクラスを作成します。
  • class StaticDocument extends Document {
    与える () {

    戻る (

    )
    }
    }

    1. Head から伸びる CustomHead を用意します。 Head クラスのライブラリで次の関数を見つけて上書きしました: getDynamicChunks、getScripts、getCssLinks:
    型を宣言 DocumentFiles = {
    sharedFiles: 読み取り専用文字列[];
    pageFiles: 読み取り専用文字列[];
    allFiles: 読み取り専用文字列[];
    };

    const タイムスタンプ = new Date().getTime();
    function getDynamicChunks(context: any, props: any, files: DocumentFiles) {
    const { dynamicImports 、 assetPrefix 、 isDevelopment 、 devOnlyCacheBusterQueryString 、 disableOptimizedLoading 、 crossOrigin 、 } = コンテキスト;
    return dynamicImports.map((ファイル:任意)=>{
    if (!file.endsWith('.js') || files.allFiles.includes(file)) は null を返します。
    console.log('ファイル', ファイル);
    return React.createElement('script', {
    非同期: !isDevelopment && disableOptimizedLoading,
    延期: !disableOptimizedLoading、
    キー: ファイル、
    src: `${assetPrefix}/_next/${encodeURI(file)}${devOnlyCacheBusterQueryString || ('?ts=' + タイムスタンプ)}`,
    ノンス: props.nonce,
    crossOrigin: props.crossOrigin || クロスオリジンcrossOrigin
    });
    });
    }

    function getScripts(context: any, props: any, files: DocumentFiles) {
    参照してみましょう。
    const { assetPrefix 、 buildManifest 、 isDevelopment 、 devOnlyCacheBusterQueryString 、 disableOptimizedLoading 、 crossOrigin 、 } = コンテキスト;
    const normalScripts = files.allFiles.filter((file)=>file.endsWith('.js'));
    const lowPriorityScripts = (ref = buildManifest.lowPriorityFiles) == null ? void 0 : ref.filter((ファイル: 任意)=>file.endsWith('.js'));
    戻る [
    ...通常のスクリプト、
    ...lowPriorityScripts
    ].map((ファイル)=>{
    return React.createElement('script', {
    キー: ファイル、
    src: `${assetPrefix}/_next/${encodeURI(file)}${devOnlyCacheBusterQueryString || ('?ts=' + タイムスタンプ)}`,
    ノンス: props.nonce,
    非同期: !isDevelopment && disableOptimizedLoading,
    延期: !disableOptimizedLoading、
    crossOrigin: props.crossOrigin || クロスオリジンcrossOrigin
    });
    });
    }

    class CustomHead extends Head {
    getDynamicChunks(files: DocumentFiles): (JSX.Element | null)[] {
    return getDynamicChunks(this.context, this.props, files);
    }
    getScripts(files: DocumentFiles): JSX.Element[] {
    return getScripts(this.context, this.props, files);
    }

    getCssLinks(ファイル: DocumentFiles) {
    const {assetPrefix、devOnlyCacheBusterQueryString、dynamicImports、crossOrigin、optimizeCss、optimizeFonts、} = this.context;
    const cssFiles = files.allFiles.filter((f)=>f.endsWith('.css'));
    const sharedFiles = new Set(files.sharedFiles);
    // 管理されていないファイルは CSS ファイルであり、
    // webpack ランタイム (`mini-css-extract-plugin`)。
    let unmangedFiles: any = new Set([]);
    let dynamicCssFiles = Array.from(new Set(dynamicImports.filter((file)=>file.endsWith('.css'))));
    if (dynamicCssFiles.length) {
    const existing = new Set(cssFiles);
    dynamicCssFiles = dynamicCssFiles.filter((f)=>!(existing.has(f) || sharedFiles.has(f)));
    unmangedFiles = new Set(dynamicCssFiles);
    cssFiles.push(...dynamicCssFiles);
    }
    let cssLinkElements: any[] = [];
    cssFiles.forEach((ファイル)=>{
    const isSharedFile = sharedFiles.has(ファイル);
    if (!optimizeCss) {
    cssLinkElements.push(React.createElement('リンク', {
    キー: `${file}-preload`,
    ノンス: this.props.nonce,
    rel: 'プリロード',
    href: `${assetPrefix}/_next/${encodeURI(file)}${devOnlyCacheBusterQueryString || ('?ts=' + タイムスタンプ)}`,
    as: 'スタイル',
    crossOrigin: this.props.crossOrigin || crossOrigin
    }));
    }
    const isUnmanagedFile = unmangedFiles.has(ファイル);
    cssLinkElements.push(/*#__PURE__*/ React.createElement('link', {
    キー: ファイル、
    ノンス: this.props.nonce,
    rel: 'スタイルシート',
    href: `${assetPrefix}/_next/${encodeURI(file)}${devOnlyCacheBusterQueryString || ('?ts=' + タイムスタンプ)}`,
    crossOrigin: this.props.crossOrigin ||クロスオリジン、
    'data-n-g': isUnmanagedFile ?未定義: isSharedFile ? '' : 未定義、
    'data-n-p': isUnmanagedFile ?未定義: isSharedFile ?未定義 : ''
    }));
    });
    if (process.env.NODE_ENV !== '開発' && optimizeFonts) {
    cssLinkElements = this.makeStylesheetInert(cssLinkElements);
    }
    cssLinkElements.length === 0 を返しますか? null : cssLinkElements;
    }
    }

    リンクを作成するすべての行を見つけ、タイムスタンプ ('?ts=' + タイムスタンプ) を含む追加のコードをプッシュしました。

    1. デバッグしたところ、npm run dev で _documents.tsx が実行されました。また、ビルドプロセスでも実行されます。ただし、_document.tsx のビルド プロセスは、a) .next フォルダーへのビルドと、b) ビルド プロセスでのみ _document.js が実行される 2 つのステップで構成されます。 npm start では呼び出されません。