kintone-migrator 0.26.1 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1786,6 +1786,18 @@ function createLocalFileCustomizationStorage(filePath) {
1786
1786
  return createLocalFileStorage(filePath, "customization config file");
1787
1787
  }
1788
1788
  //#endregion
1789
+ //#region src/core/adapters/local/fileContentReader.ts
1790
+ var LocalFileContentReader = class {
1791
+ async read(filePath) {
1792
+ try {
1793
+ const buffer = await readFile(filePath);
1794
+ return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
1795
+ } catch (error) {
1796
+ throw new SystemError(SystemErrorCode.StorageError, `Failed to read file: ${filePath}`, error);
1797
+ }
1798
+ }
1799
+ };
1800
+ //#endregion
1789
1801
  //#region src/core/adapters/local/fileWriter.ts
1790
1802
  var LocalFileWriter = class {
1791
1803
  constructor(baseDir = process.cwd()) {
@@ -1845,6 +1857,7 @@ function createCustomizationCliContainer(config) {
1845
1857
  customizationStorage: createLocalFileCustomizationStorage(config.customizeFilePath),
1846
1858
  fileUploader: new KintoneFileUploader(client, process.cwd()),
1847
1859
  fileDownloader: new KintoneFileDownloader(client),
1860
+ fileContentReader: new LocalFileContentReader(),
1848
1861
  fileWriter: new LocalFileWriter(process.cwd()),
1849
1862
  appDeployer: new KintoneAppDeployer(client, config.appId)
1850
1863
  };
@@ -3791,7 +3804,7 @@ function remoteResourceName(resource) {
3791
3804
  if (resource.type === "URL") return resource.url;
3792
3805
  return resource.file.name;
3793
3806
  }
3794
- function compareResourceLists(localResources, remoteResources, platform, resourceType, warnings) {
3807
+ function compareResourceLists(localResources, remoteResources, platform, resourceType, warnings, modifiedFileNames) {
3795
3808
  const entries = [];
3796
3809
  const localNames = localResources.map(resourceName);
3797
3810
  const remoteNames = remoteResources.map(remoteResourceName);
@@ -3823,10 +3836,13 @@ function compareResourceLists(localResources, remoteResources, platform, resourc
3823
3836
  details: `removed ${resType} resource`
3824
3837
  });
3825
3838
  }
3826
- const matchedFiles = [...localNameSet].filter((n) => remoteNameSet.has(n));
3827
- const hasLocalFiles = localResources.some((r) => r.type === "FILE");
3828
- const hasRemoteFiles = remoteResources.some((r) => r.type === "FILE");
3829
- if (matchedFiles.length > 0 && hasLocalFiles && hasRemoteFiles) warnings.push(`[${platform}.${resourceType}] FILE resources are compared by name only; content changes are not detected`);
3839
+ for (const name of localNameSet) if (remoteNameSet.has(name) && localTypeMap.get(name) === "FILE" && remoteTypeMap.get(name) === "FILE" && modifiedFileNames.has(name)) entries.push({
3840
+ type: "modified",
3841
+ platform,
3842
+ category: resourceType,
3843
+ name,
3844
+ details: "file content changed"
3845
+ });
3830
3846
  if (!hasDuplicates) {
3831
3847
  const localShared = localNames.filter((n) => remoteNameSet.has(n));
3832
3848
  const remoteShared = remoteNames.filter((n) => localNameSet.has(n));
@@ -3840,10 +3856,10 @@ function compareResourceLists(localResources, remoteResources, platform, resourc
3840
3856
  }
3841
3857
  return entries;
3842
3858
  }
3843
- function comparePlatform(localJs, localCss, remote, platform, warnings) {
3844
- return [...compareResourceLists(localJs, remote.js, platform, "js", warnings), ...compareResourceLists(localCss, remote.css, platform, "css", warnings)];
3859
+ function comparePlatform(localJs, localCss, remote, platform, warnings, modifiedFileNames) {
3860
+ return [...compareResourceLists(localJs, remote.js, platform, "js", warnings, modifiedFileNames), ...compareResourceLists(localCss, remote.css, platform, "css", warnings, modifiedFileNames)];
3845
3861
  }
3846
- const CustomizationDiffDetector = { detect: (local, remote) => {
3862
+ const CustomizationDiffDetector = { detect: (local, remote, modifiedFileNames) => {
3847
3863
  const entries = [];
3848
3864
  const warnings = [];
3849
3865
  const localScope = local.scope ?? "ALL";
@@ -3854,20 +3870,55 @@ const CustomizationDiffDetector = { detect: (local, remote) => {
3854
3870
  name: "scope",
3855
3871
  details: `${remote.scope} -> ${localScope}`
3856
3872
  });
3857
- entries.push(...comparePlatform(local.desktop.js, local.desktop.css, remote.desktop, "desktop", warnings));
3858
- entries.push(...comparePlatform(local.mobile.js, local.mobile.css, remote.mobile, "mobile", warnings));
3873
+ entries.push(...comparePlatform(local.desktop.js, local.desktop.css, remote.desktop, "desktop", warnings, modifiedFileNames));
3874
+ entries.push(...comparePlatform(local.mobile.js, local.mobile.css, remote.mobile, "mobile", warnings, modifiedFileNames));
3859
3875
  return buildDiffResult(entries, warnings);
3860
3876
  } };
3861
3877
  //#endregion
3862
3878
  //#region src/core/application/customization/detectCustomizationDiff.ts
3863
- async function detectCustomizationDiff({ container }) {
3864
- return detectDiffFromConfig({
3865
- getStorage: () => container.customizationStorage.get(),
3866
- fetchRemote: () => container.customizationConfigurator.getCustomization(),
3867
- parseConfig: (content) => parseCustomizationConfigText(container.configCodec, content),
3868
- detect: (local, remote) => CustomizationDiffDetector.detect(local, remote),
3869
- notFoundMessage: "Customization config file not found"
3870
- });
3879
+ /**
3880
+ * Finds matched FILE resource pairs (local FILE + remote FILE with same basename)
3881
+ * across all platforms and categories.
3882
+ */
3883
+ function findMatchedFilePairs(localResources, remoteResources) {
3884
+ const pairs = [];
3885
+ const remoteFileMap = /* @__PURE__ */ new Map();
3886
+ for (const r of remoteResources) if (r.type === "FILE") remoteFileMap.set(remoteResourceName(r), r.file.fileKey);
3887
+ for (const local of localResources) {
3888
+ if (local.type !== "FILE") continue;
3889
+ const name = resourceName(local);
3890
+ const remoteFileKey = remoteFileMap.get(name);
3891
+ if (remoteFileKey !== void 0) pairs.push({
3892
+ localResource: local,
3893
+ remoteFileKey
3894
+ });
3895
+ }
3896
+ return pairs;
3897
+ }
3898
+ async function detectCustomizationDiff({ container, input }) {
3899
+ const [storageResult, remote] = await Promise.all([container.customizationStorage.get(), container.customizationConfigurator.getCustomization()]);
3900
+ if (!storageResult.exists) throw new ValidationError(ValidationErrorCode.InvalidInput, "Customization config file not found");
3901
+ const local = parseCustomizationConfigText(container.configCodec, storageResult.content);
3902
+ const allPairs = [
3903
+ ...findMatchedFilePairs(local.desktop.js, remote.desktop.js),
3904
+ ...findMatchedFilePairs(local.desktop.css, remote.desktop.css),
3905
+ ...findMatchedFilePairs(local.mobile.js, remote.mobile.js),
3906
+ ...findMatchedFilePairs(local.mobile.css, remote.mobile.css)
3907
+ ];
3908
+ const modifiedFileNames = /* @__PURE__ */ new Set();
3909
+ if (allPairs.length > 0) {
3910
+ const results = await Promise.all(allPairs.map(async ({ localResource, remoteFileKey }) => {
3911
+ const localPath = join(input.basePath, localResource.path);
3912
+ const [localContent, remoteContent] = await Promise.all([container.fileContentReader.read(localPath), container.fileDownloader.download(remoteFileKey)]);
3913
+ const isEqual = Buffer.from(localContent).equals(Buffer.from(remoteContent));
3914
+ return {
3915
+ name: resourceName(localResource),
3916
+ isEqual
3917
+ };
3918
+ }));
3919
+ for (const { name, isEqual } of results) if (!isEqual) modifiedFileNames.add(name);
3920
+ }
3921
+ return CustomizationDiffDetector.detect(local, remote, modifiedFileNames);
3871
3922
  }
3872
3923
  //#endregion
3873
3924
  //#region src/cli/customizeConfig.ts
@@ -4076,6 +4127,10 @@ async function saveCustomization({ container, input }) {
4076
4127
  }
4077
4128
  //#endregion
4078
4129
  //#region src/cli/commands/customize/capture.ts
4130
+ function computeBasePath(customizeFilePath) {
4131
+ const filePrefix = deriveFilePrefix(customizeFilePath);
4132
+ return join(dirname(resolve(customizeFilePath)), filePrefix);
4133
+ }
4079
4134
  function deriveFilePrefix(customizeFilePath) {
4080
4135
  const resolved = resolve(customizeFilePath);
4081
4136
  const fileName = basename(resolved, extname(resolved));
@@ -4134,11 +4189,9 @@ var capture_default$10 = define({
4134
4189
  //#endregion
4135
4190
  //#region src/cli/commands/customize/apply.ts
4136
4191
  function createCustomizationContainer(config) {
4137
- const container = createCustomizationCliContainer(config);
4138
- const filePrefix = deriveFilePrefix(config.customizeFilePath);
4139
4192
  return {
4140
- container,
4141
- basePath: join(dirname(resolve(config.customizeFilePath)), filePrefix)
4193
+ container: createCustomizationCliContainer(config),
4194
+ basePath: computeBasePath(config.customizeFilePath)
4142
4195
  };
4143
4196
  }
4144
4197
  async function runCustomizationApply(container, basePath) {
@@ -4161,127 +4214,166 @@ async function runDiffPreview(config) {
4161
4214
  s.start("Detecting changes...");
4162
4215
  let result;
4163
4216
  try {
4164
- result = await detectCustomizationDiff({ container });
4217
+ result = await detectCustomizationDiff({
4218
+ container,
4219
+ input: { basePath }
4220
+ });
4165
4221
  } catch (error) {
4166
4222
  s.stop("Comparison failed.");
4167
4223
  throw error;
4168
4224
  }
4169
4225
  s.stop("Comparison complete.");
4170
4226
  printCustomizationDiffResult(result);
4171
- const hasFileContentWarning = result.warnings.some((w) => w.includes("content changes are not detected"));
4172
4227
  return {
4173
4228
  container,
4174
4229
  basePath,
4175
- hasChanges: !result.isEmpty || hasFileContentWarning
4230
+ hasChanges: !result.isEmpty
4176
4231
  };
4177
4232
  }
4233
+ var apply_default$9 = define({
4234
+ name: "apply",
4235
+ description: "Apply JS/CSS customization to kintone app",
4236
+ args: {
4237
+ ...customizeArgs,
4238
+ ...confirmArgs
4239
+ },
4240
+ run: async (ctx) => {
4241
+ try {
4242
+ const values = ctx.values;
4243
+ const skipConfirm = values.yes === true;
4244
+ await routeMultiApp(values, {
4245
+ singleLegacy: async () => {
4246
+ const { container, basePath, hasChanges } = await runDiffPreview(resolveCustomizeConfig(values));
4247
+ if (!hasChanges) {
4248
+ p.log.success("No changes detected.");
4249
+ return;
4250
+ }
4251
+ if (!skipConfirm) {
4252
+ const shouldContinue = await p.confirm({ message: "Apply these changes?" });
4253
+ if (p.isCancel(shouldContinue) || !shouldContinue) {
4254
+ p.cancel("Apply cancelled.");
4255
+ return;
4256
+ }
4257
+ }
4258
+ await runCustomizationApply(container, basePath);
4259
+ await confirmAndDeploy([container], skipConfirm, "Customization applied and deployed successfully.");
4260
+ },
4261
+ singleApp: async (app, projectConfig) => {
4262
+ const { container, basePath, hasChanges } = await runDiffPreview(resolveCustomizeAppConfig(app, projectConfig, values));
4263
+ if (!hasChanges) {
4264
+ p.log.success("No changes detected.");
4265
+ return;
4266
+ }
4267
+ if (!skipConfirm) {
4268
+ const shouldContinue = await p.confirm({ message: "Apply these changes?" });
4269
+ if (p.isCancel(shouldContinue) || !shouldContinue) {
4270
+ p.cancel("Apply cancelled.");
4271
+ return;
4272
+ }
4273
+ }
4274
+ await runCustomizationApply(container, basePath);
4275
+ await confirmAndDeploy([container], skipConfirm, "Customization applied and deployed successfully.");
4276
+ },
4277
+ multiApp: async (plan, projectConfig) => {
4278
+ const appDiffResults = [];
4279
+ for (const app of plan.orderedApps) {
4280
+ const config = resolveCustomizeAppConfig(app, projectConfig, values);
4281
+ printAppHeader(app.name, app.appId);
4282
+ const { container, basePath, hasChanges } = await runDiffPreview(config);
4283
+ appDiffResults.push({
4284
+ app,
4285
+ container,
4286
+ basePath,
4287
+ hasChanges
4288
+ });
4289
+ }
4290
+ if (!appDiffResults.some((a) => a.hasChanges)) {
4291
+ p.log.success("No changes detected in any app.");
4292
+ return;
4293
+ }
4294
+ if (!skipConfirm) {
4295
+ const shouldContinue = await p.confirm({ message: "Apply these changes to all apps?" });
4296
+ if (p.isCancel(shouldContinue) || !shouldContinue) {
4297
+ p.cancel("Apply cancelled.");
4298
+ return;
4299
+ }
4300
+ }
4301
+ const containers = [];
4302
+ await runMultiAppWithHeaders(plan, async (app) => {
4303
+ const entry = appDiffResults.find((a) => a.app.name === app.name);
4304
+ if (!entry) throw new SystemError(SystemErrorCode.InternalServerError, `App container not found for "${app.name}"`);
4305
+ if (!entry.hasChanges) {
4306
+ p.log.info("No changes. Skipping.");
4307
+ return;
4308
+ }
4309
+ await runCustomizationApply(entry.container, entry.basePath);
4310
+ containers.push({
4311
+ appDeployer: entry.container.appDeployer,
4312
+ appName: app.name
4313
+ });
4314
+ });
4315
+ await confirmAndDeploy(containers, skipConfirm, "Customization applied and deployed successfully.");
4316
+ }
4317
+ });
4318
+ } catch (error) {
4319
+ handleCliError(error);
4320
+ }
4321
+ }
4322
+ });
4323
+ //#endregion
4324
+ //#region src/cli/commands/customize/diff.ts
4325
+ async function runDiff$1(containerConfig) {
4326
+ const container = createCustomizationCliContainer(containerConfig);
4327
+ const basePath = computeBasePath(containerConfig.customizeFilePath);
4328
+ const s = p.spinner();
4329
+ s.start("Comparing customization settings...");
4330
+ let result;
4331
+ try {
4332
+ result = await detectCustomizationDiff({
4333
+ container,
4334
+ input: { basePath }
4335
+ });
4336
+ } catch (error) {
4337
+ s.stop("Comparison failed.");
4338
+ throw error;
4339
+ }
4340
+ s.stop("Comparison complete.");
4341
+ printCustomizationDiffResult(result);
4342
+ }
4178
4343
  //#endregion
4179
4344
  //#region src/cli/commands/customize/index.ts
4180
4345
  var customize_default = define({
4181
4346
  name: "customize",
4182
4347
  description: "Manage kintone JS/CSS customizations",
4183
4348
  subCommands: {
4184
- apply: define({
4185
- name: "apply",
4186
- description: "Apply JS/CSS customization to kintone app",
4187
- args: {
4188
- ...customizeArgs,
4189
- ...confirmArgs
4190
- },
4349
+ apply: apply_default$9,
4350
+ capture: capture_default$10,
4351
+ diff: define({
4352
+ name: "diff",
4353
+ description: "Compare local customization config with remote kintone app",
4354
+ args: customizeArgs,
4191
4355
  run: async (ctx) => {
4192
4356
  try {
4193
4357
  const values = ctx.values;
4194
- const skipConfirm = values.yes === true;
4195
4358
  await routeMultiApp(values, {
4196
4359
  singleLegacy: async () => {
4197
- const { container, basePath, hasChanges } = await runDiffPreview(resolveCustomizeConfig(values));
4198
- if (!hasChanges) {
4199
- p.log.success("No changes detected.");
4200
- return;
4201
- }
4202
- if (!skipConfirm) {
4203
- const shouldContinue = await p.confirm({ message: "Apply these changes?" });
4204
- if (p.isCancel(shouldContinue) || !shouldContinue) {
4205
- p.cancel("Apply cancelled.");
4206
- return;
4207
- }
4208
- }
4209
- await runCustomizationApply(container, basePath);
4210
- await confirmAndDeploy([container], skipConfirm, "Customization applied and deployed successfully.");
4360
+ await runDiff$1(resolveCustomizeConfig(values));
4211
4361
  },
4212
4362
  singleApp: async (app, projectConfig) => {
4213
- const { container, basePath, hasChanges } = await runDiffPreview(resolveCustomizeAppConfig(app, projectConfig, values));
4214
- if (!hasChanges) {
4215
- p.log.success("No changes detected.");
4216
- return;
4217
- }
4218
- if (!skipConfirm) {
4219
- const shouldContinue = await p.confirm({ message: "Apply these changes?" });
4220
- if (p.isCancel(shouldContinue) || !shouldContinue) {
4221
- p.cancel("Apply cancelled.");
4222
- return;
4223
- }
4224
- }
4225
- await runCustomizationApply(container, basePath);
4226
- await confirmAndDeploy([container], skipConfirm, "Customization applied and deployed successfully.");
4363
+ await runDiff$1(resolveCustomizeAppConfig(app, projectConfig, values));
4227
4364
  },
4228
4365
  multiApp: async (plan, projectConfig) => {
4229
- const appDiffResults = [];
4230
- for (const app of plan.orderedApps) {
4231
- const config = resolveCustomizeAppConfig(app, projectConfig, values);
4366
+ await runMultiAppWithFailCheck(plan, async (app) => {
4367
+ const containerConfig = resolveCustomizeAppConfig(app, projectConfig, values);
4232
4368
  printAppHeader(app.name, app.appId);
4233
- const { container, basePath, hasChanges } = await runDiffPreview(config);
4234
- appDiffResults.push({
4235
- app,
4236
- container,
4237
- basePath,
4238
- hasChanges
4239
- });
4240
- }
4241
- if (!appDiffResults.some((a) => a.hasChanges)) {
4242
- p.log.success("No changes detected in any app.");
4243
- return;
4244
- }
4245
- if (!skipConfirm) {
4246
- const shouldContinue = await p.confirm({ message: "Apply these changes to all apps?" });
4247
- if (p.isCancel(shouldContinue) || !shouldContinue) {
4248
- p.cancel("Apply cancelled.");
4249
- return;
4250
- }
4251
- }
4252
- const containers = [];
4253
- await runMultiAppWithHeaders(plan, async (app) => {
4254
- const entry = appDiffResults.find((a) => a.app.name === app.name);
4255
- if (!entry) throw new SystemError(SystemErrorCode.InternalServerError, `App container not found for "${app.name}"`);
4256
- if (!entry.hasChanges) {
4257
- p.log.info("No changes. Skipping.");
4258
- return;
4259
- }
4260
- await runCustomizationApply(entry.container, entry.basePath);
4261
- containers.push({
4262
- appDeployer: entry.container.appDeployer,
4263
- appName: app.name
4264
- });
4265
- });
4266
- await confirmAndDeploy(containers, skipConfirm, "Customization applied and deployed successfully.");
4369
+ await runDiff$1(containerConfig);
4370
+ }, "All customization diffs completed successfully.");
4267
4371
  }
4268
4372
  });
4269
4373
  } catch (error) {
4270
4374
  handleCliError(error);
4271
4375
  }
4272
4376
  }
4273
- }),
4274
- capture: capture_default$10,
4275
- diff: createDiffCommand({
4276
- description: "Compare local customization config with remote kintone app",
4277
- args: customizeArgs,
4278
- spinnerMessage: "Comparing customization settings...",
4279
- multiAppSuccessMessage: "All customization diffs completed successfully.",
4280
- createContainer: createCustomizationCliContainer,
4281
- detectDiff: detectCustomizationDiff,
4282
- printResult: printCustomizationDiffResult,
4283
- resolveContainerConfig: resolveCustomizeConfig,
4284
- resolveAppContainerConfig: resolveCustomizeAppConfig
4285
4377
  })
4286
4378
  },
4287
4379
  run: () => {}