From 07bc14ec9072bc10e67eabc614f077dfdcccbd3f Mon Sep 17 00:00:00 2001 From: hikaruhuimin Date: Thu, 14 May 2026 22:14:10 +0900 Subject: [PATCH 1/2] feat(plugin-vscode): short-circuit dry-run before vsce exec, write vscode-package-plan.json --- packages/targets/plugin-vscode/src/index.ts | 31 +++++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/packages/targets/plugin-vscode/src/index.ts b/packages/targets/plugin-vscode/src/index.ts index 6e0cf82..0afa629 100644 --- a/packages/targets/plugin-vscode/src/index.ts +++ b/packages/targets/plugin-vscode/src/index.ts @@ -1,5 +1,6 @@ import { defineTarget, setupGuide, exec } from '@profullstack/sh1pt-core'; import { join } from 'node:path'; +import { writeFileSync } from 'node:fs'; interface Config { publisher: string; // e.g. "mycompany" @@ -14,6 +15,31 @@ export default defineTarget({ label: 'VS Code Marketplace', async build(ctx, config) { + const pkgDir = config.packageDir ? join(ctx.projectDir, config.packageDir) : ctx.projectDir; + const expectedVsix = `${ctx.outDir}/${config.extensionName}-${ctx.version}.vsix`; + + // Short-circuit dry-run builds before any vsce/npm exec checks + if (ctx.dryRun) { + const args = ['--yes', 'vsce', 'package', '--out', ctx.outDir]; + if (config.target) args.push('--target', config.target); + const plan = { + target: 'plugin-vscode', + version: ctx.version, + channel: ctx.channel, + packageDir: pkgDir, + expectedVsix, + publisher: config.publisher, + extensionName: config.extensionName, + targetPlatform: config.target ?? null, + vsceCommand: { cmd: 'npx', args }, + }; + const planPath = join(ctx.outDir, 'vscode-package-plan.json'); + writeFileSync(planPath, JSON.stringify(plan, null, 2)); + ctx.log(`dry-run: wrote ${planPath}`); + return { artifact: expectedVsix, meta: { plan } }; + } + + // Real build path — verify CLI availability then package ctx.log('vsce: verifying CLI availability'); try { @@ -25,19 +51,18 @@ export default defineTarget({ }); } - const pkgDir = config.packageDir ? join(ctx.projectDir, config.packageDir) : ctx.projectDir; ctx.log(`vsce: packaging ${config.publisher}.${config.extensionName} v${ctx.version}`); const args = ['--yes', 'vsce', 'package', '--out', ctx.outDir]; if (config.target) args.push('--target', config.target); - const { stdout } = await exec('npx', args, { + await exec('npx', args, { cwd: pkgDir, log: ctx.log, throwOnNonZero: true, }); - return { artifact: `${ctx.outDir}/${config.extensionName}-${ctx.version}.vsix` }; + return { artifact: expectedVsix }; }, async ship(ctx, config) { From 2cdd14fc0b28be44fe5fac7c87153c4275492712 Mon Sep 17 00:00:00 2001 From: hikaruhuimin Date: Thu, 14 May 2026 22:14:12 +0900 Subject: [PATCH 2/2] test(plugin-vscode): verify dry-run skips exec and writes plan --- .../targets/plugin-vscode/src/index.test.ts | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/targets/plugin-vscode/src/index.test.ts b/packages/targets/plugin-vscode/src/index.test.ts index fdb54f0..d3c949c 100644 --- a/packages/targets/plugin-vscode/src/index.test.ts +++ b/packages/targets/plugin-vscode/src/index.test.ts @@ -1,4 +1,39 @@ -import { smokeTest } from '@profullstack/sh1pt-core/testing'; +import { describe, it, expect, vi } from 'vitest'; import adapter from './index.js'; +import { fakeBuildContext, fakeShipContext } from '@profullstack/sh1pt-core/testing'; -smokeTest(adapter, { idPrefix: 'plugin', requireKind: true }); +const config = { publisher: 'mycompany', extensionName: 'my-extension' }; + +describe('plugin-vscode adapter', () => { + it('exports correct id and label', () => { + expect(adapter.id).toBe('plugin-vscode'); + expect(adapter.label).toBeTruthy(); + }); + + it('short-circuits dry-run build before vsce/npm exec checks', async () => { + const writeSpy = vi.spyOn(await import('node:fs'), 'writeFileSync').mockImplementation(() => {}); + const execSpy = vi.spyOn(await import('@profullstack/sh1pt-core'), 'exec').mockImplementation(async () => ({ exitCode: 0, stdout: '', stderr: '' })); + const ctx = fakeBuildContext({ dryRun: true }); + const result = await adapter.build(ctx, config); + // dry-run should NOT call exec + expect(execSpy).not.toHaveBeenCalled(); + // dry-run should write the plan file + expect(writeSpy).toHaveBeenCalled(); + const planArg = writeSpy.mock.calls[0][1]; + const plan = JSON.parse(planArg); + expect(plan.target).toBe('plugin-vscode'); + expect(plan.packageDir).toBeDefined(); + expect(plan.expectedVsix).toContain('my-extension'); + expect(plan.vsceCommand).toBeDefined(); + expect(plan.vsceCommand.cmd).toBe('npx'); + expect(result.meta?.plan).toBeDefined(); + writeSpy.mockRestore(); + execSpy.mockRestore(); + }); + + it('dry-run ship returns early', async () => { + const ctx = fakeShipContext({ dryRun: true, secret: () => 'fake-token' }); + const result = await adapter.ship(ctx, config); + expect(result.id).toBe('dry-run'); + }); +});