kintone プラグイン一括登録・更新用リスト作成

[kintone プラグイン一括登録・更新](https://qiita.com/rex0220/items/a0570f40c0be78b3907a)用に、プラグイン開発ディレクトリから一覧を作成します。

# 概要

下記のようなプラグイン開発ディレクトリからプラグインリストを作成します。
複数プラグインだとリストを手作業で作成するのも大変です。
コードは、ChatGPT にほとんど作ってもらい、最後の出力の並びを訂正したくらいです。

- プラグイン開発ディレクトリ

![2024-07-20_16h32_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/fe76ee68-d76a-58f7-e30c-07cccaa2c660.png)

- 作成するプラグインリスト
    - プラグインファイルパス、プラグインID、プラグイン名

```.txt
node scripts/plugin-uploader.js -f aggregate/dist/aggregate-plugin56.zip  --id dnfmecefagpmheminfphakcfaoephohi  # アプリ集計
node scripts/plugin-uploader.js -f autocomplete/dist/autocomplete-plugin2.zip  --id oekapkklfjhakkcfefjpldkbpkialkof  # 自動補完
node scripts/plugin-uploader.js -f autolookup/dist/autolookup-plugin8.zip  --id ioagfjojflfaoomnphdpkfgjeghohebj  # ルックアップ自動取得

```

# listPluginFiles.js 実行方法

標準出力をテキストファイルに書き込みます。
エラーは、コンソールに表示されます。

```.log
> node .\scripts\listPluginFiles.js > ./scripts/testList1.txt
Warning: pluginId.txt does not exist. Skipping directory ..
Warning: action-file\pluginId.txt does not exist. Skipping directory action-file.
Warning: edit-lookup\pluginId.txt does not exist. Skipping directory edit-lookup.
```

# listPluginFiles.js

開発環境にあわせて、searchString ('upload-prod')等を変えると他でも使えると思います。

```listPluginFiles.js
const fs = require('fs').promises;
const path = require('path');
const fg = require('fast-glob');

const searchString = 'upload-prod';
const zipPattern = /dist\/.*\.zip/;
const pattern = './**/package.json';

/**
 * Finds all package.json files matching the given pattern.
 * @returns {Promise<string[]>} A promise that resolves to an array of file paths.
 */
async function findPackageJsonFiles() {
  const files = await fg(pattern);
  files.sort((a, b) => path.dirname(a).localeCompare(path.dirname(b)));
  return files;
}

/**
 * Processes a given package.json file, extracting the plugin ID and finding zip file paths.
 * @param {string} file - The path to the package.json file.
 */
async function processPackageJsonFile(file) {
  const dir = path.dirname(file);
  const pluginIdFile = path.join(dir, 'pluginId.txt');
  const manifestFile = path.join(dir, 'prod', 'manifest.json');

  if (!(await fileExists(pluginIdFile))) {
    console.warn(`Warning: ${pluginIdFile} does not exist. Skipping directory ${dir}.`);
    return;
  }

  if (!(await fileExists(manifestFile))) {
    console.warn(`Warning: ${manifestFile} does not exist. Skipping directory ${dir}.`);
    return;
  }

  try {
    const pluginId = await fs.readFile(pluginIdFile, 'utf8');
    const manifestData = await fs.readFile(manifestFile, 'utf8');
    const manifest = JSON.parse(manifestData);
    const pluginName = manifest.name.ja;
    const data = await fs.readFile(file, 'utf8');
    findZipFiles(data, file, pluginId, pluginName);
  } catch (err) {
    console.error(`Error processing files in directory ${dir}:`, err);
  }
}

/**
 * Checks if a file exists.
 * @param {string} filePath - The path to the file.
 * @returns {Promise<boolean>} A promise that resolves to true if the file exists, false otherwise.
 */
async function fileExists(filePath) {
  try {
    await fs.access(filePath);
    return true;
  } catch {
    return false;
  }
}

/**
 * Finds zip file paths in the package.json data that match the search string.
 * @param {string} data - The contents of the package.json file.
 * @param {string} file - The path to the package.json file.
 * @param {string} pluginId - The ID of the plugin extracted from pluginId.txt.
 * @param {string} pluginName - The name of the plugin extracted from manifest.json.
 */
function findZipFiles(data, file, pluginId, pluginName) {
  const lines = data.split('\n');
  lines.forEach(line => {
    if (line.includes(searchString)) {
      const match = line.match(zipPattern);
      if (match) {
        const filePath = path.dirname(file) + '/' + match[0];
        console.log(`node scripts/plugin-uploader.js -f ${filePath}  --id ${pluginId.trim()}  # ${pluginName}`);
      }
    }
  });
}

/**
 * Main function that initiates the process of finding and processing package.json files.
 */
async function main() {
  try {
    const files = await findPackageJsonFiles();
    for (const file of files) {
      await processPackageJsonFile(file);
    }
  } catch (err) {
    console.error('Error finding or processing files:', err);
  }
}

// Execute the main function
main();
```