Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/fix-bundler-transitive-deps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@kidd-cli/bundler': minor
---

Revert Bun.build migration and restore tsdown as the bundler.

- Restore `map-config.ts` with tsdown InlineConfig mapping (build + watch)
- Restore simpler autoload plugin (no `coreDistDir` needed)
- Restore tsdown native watch mode
- Remove `@kidd-cli/core` dependency (no longer needed)
- Remove `bun-runner.ts` subprocess architecture
- Restore `NODE_BUILTINS` and `neverBundle` for proper externalization
2 changes: 1 addition & 1 deletion .changeset/upgrade-deps.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
"@kidd-cli/core": minor
'@kidd-cli/core': minor
---

Upgrade `@clack/prompts` from 1.1.0 to 1.2.0
8 changes: 3 additions & 5 deletions packages/bundler/package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "@kidd-cli/bundler",
"version": "0.6.0",
"description": "Programmatic bundler for kidd CLI tools powered by Bun",
"description": "Programmatic bundler for kidd CLI tools powered by tsdown",
"keywords": [
"bun",
"bundler",
"cli",
"kidd",
"tsdown",
"typescript"
],
"homepage": "https://github.com/joggrdocs/kidd/tree/main/packages/bundler",
Expand Down Expand Up @@ -44,17 +44,15 @@
"@kidd-cli/utils": "workspace:*",
"es-toolkit": "catalog:",
"ts-pattern": "catalog:",
"tsdown": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@types/bun": "catalog:",
"@types/node": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:",
"vitest": "catalog:"
},
"engines": {
"bun": ">=1.3",
"node": ">=24"
}
}
186 changes: 99 additions & 87 deletions packages/bundler/src/autoloader/autoload-plugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,102 +2,60 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'

vi.mock(import('./generate-autoloader.js'))
vi.mock(import('./scan-commands.js'))
vi.mock(import('node:fs'))

const { generateStaticAutoloader } = await import('./generate-autoloader.js')
const { scanCommandsDir } = await import('./scan-commands.js')
const { readFileSync } = await import('node:fs')
const { createAutoloadPlugin } = await import('./autoload-plugin.js')

const mockGenerateStaticAutoloader = vi.mocked(generateStaticAutoloader)
const mockScanCommandsDir = vi.mocked(scanCommandsDir)
const mockReadFileSync = vi.mocked(readFileSync)

beforeEach(() => {
vi.clearAllMocks()
})

describe('createAutoloadPlugin', () => {
const mockBuild = {
onResolve: vi.fn(),
onLoad: vi.fn(),
}

beforeEach(() => {
const plugin = createAutoloadPlugin({
commandsDir: '/project/commands',
tagModulePath: '/project/tag.js',
coreDistDir: '/project/node_modules/@kidd-cli/core/dist',
})

plugin.setup(mockBuild as never)
})

describe('onResolve hook', () => {
it('should resolve virtual module ID', () => {
const [, resolveFn] = mockBuild.onResolve.mock.calls[0]
const result = resolveFn({ path: 'virtual:kidd-static-commands' })

expect(result).toEqual({
namespace: 'kidd-autoload',
path: 'virtual:kidd-static-commands',
describe('transform hook', () => {
it('should return null for non-kidd dist files', () => {
const plugin = createAutoloadPlugin({
commandsDir: '/project/commands',
tagModulePath: '/project/tag.js',
})
})
})

describe('onLoad autoload hook', () => {
it('should call scanCommandsDir and generateStaticAutoloader for virtual module', async () => {
const scanResult = { dirs: [], files: [] }
mockScanCommandsDir.mockResolvedValueOnce(scanResult)
mockGenerateStaticAutoloader.mockReturnValueOnce('generated code')

const autoloadCall = mockBuild.onLoad.mock.calls.find(
([opts]) => opts.namespace === 'kidd-autoload'
)
const result = await autoloadCall[1]({})
const result = plugin.transform('some code', '/other/package/dist/index.js')

expect(result).toEqual({ contents: 'generated code', loader: 'js' })
expect(mockScanCommandsDir).toHaveBeenCalledOnce()
expect(mockGenerateStaticAutoloader).toHaveBeenCalledOnce()
expect(result).toBeNull()
})

it('should pass commandsDir and tagModulePath to generators', async () => {
const scanResult = { dirs: [], files: [] }
mockScanCommandsDir.mockResolvedValueOnce(scanResult)
mockGenerateStaticAutoloader.mockReturnValueOnce('generated code')

const autoloadCall = mockBuild.onLoad.mock.calls.find(
([opts]) => opts.namespace === 'kidd-autoload'
)
await autoloadCall[1]({})

expect(mockScanCommandsDir).toHaveBeenCalledWith('/project/commands')
expect(mockGenerateStaticAutoloader).toHaveBeenCalledWith({
scan: scanResult,
it('should return null when no region start marker found', () => {
const plugin = createAutoloadPlugin({
commandsDir: '/project/commands',
tagModulePath: '/project/tag.js',
})
})
})

describe('onLoad transform hook', () => {
it('should return undefined when no region start marker found', () => {
const transformCall = mockBuild.onLoad.mock.calls.find(
([opts]) => opts.namespace !== 'kidd-autoload'
)
const code = 'const x = 1\n//#endregion\n'
const result = plugin.transform(code, '/node_modules/kidd/dist/index.js')

mockReadFileSync.mockReturnValueOnce('const x = 1\nconst y = 2\n')
expect(result).toBeNull()
})

const result = transformCall[1]({
path: '/project/node_modules/@kidd-cli/core/dist/index.js',
it('should return null when no region end marker found', () => {
const plugin = createAutoloadPlugin({
commandsDir: '/project/commands',
tagModulePath: '/project/tag.js',
})

expect(result).toBeUndefined()
const code = 'const x = 1\n//#region src/autoload.ts\nsome content'
const result = plugin.transform(code, '/node_modules/kidd/dist/index.js')

expect(result).toBeNull()
})

it('should replace region with static import when markers found', () => {
const transformCall = mockBuild.onLoad.mock.calls.find(
([opts]) => opts.namespace !== 'kidd-autoload'
)
it('should replace region with static import when markers found in kidd dist', () => {
const plugin = createAutoloadPlugin({
commandsDir: '/project/commands',
tagModulePath: '/project/tag.js',
})

const code = [
'const before = 1',
Expand All @@ -107,33 +65,87 @@ describe('createAutoloadPlugin', () => {
'const after = 2',
].join('\n')

mockReadFileSync.mockReturnValueOnce(code)
const result = plugin.transform(code, '/node_modules/kidd/dist/index.js')

expect(result).toContain('const before = 1')
expect(result).toContain('const after = 2')
expect(result).toContain('//#region src/autoload.ts (static)')
expect(result).toContain("await import('virtual:kidd-static-commands')")
expect(result).toContain('return mod.autoload()')
expect(result).not.toContain('async function autoload() { return {} }')
})
})

describe('resolveId hook', () => {
it('should resolve virtual module ID to prefixed ID', () => {
const plugin = createAutoloadPlugin({
commandsDir: '/project/commands',
tagModulePath: '/project/tag.js',
})

const result = plugin.resolveId('virtual:kidd-static-commands')

expect(result).toBe('\0virtual:kidd-static-commands')
})

it('should return null for non-virtual module IDs', () => {
const plugin = createAutoloadPlugin({
commandsDir: '/project/commands',
tagModulePath: '/project/tag.js',
})

const result = plugin.resolveId('./some-module.js')

expect(result).toBeNull()
})
})

const result = transformCall[1]({
path: '/project/node_modules/@kidd-cli/core/dist/index.js',
describe('load hook', () => {
it('should return null for non-virtual module IDs', async () => {
const plugin = createAutoloadPlugin({
commandsDir: '/project/commands',
tagModulePath: '/project/tag.js',
})

expect(result).toEqual({ contents: expect.any(String), loader: 'js' })
expect(result.contents).toContain('const before = 1')
expect(result.contents).toContain('const after = 2')
expect(result.contents).toContain('//#region src/autoload.ts (static)')
expect(result.contents).toContain("await import('virtual:kidd-static-commands')")
expect(result.contents).toContain('return mod.autoload()')
expect(result.contents).not.toContain('async function autoload() { return {} }')
const result = await plugin.load('./some-module.js')

expect(result).toBeNull()
})

it('should return undefined when code has end marker but no start marker', () => {
const transformCall = mockBuild.onLoad.mock.calls.find(
([opts]) => opts.namespace !== 'kidd-autoload'
)
it('should call scanCommandsDir and generateStaticAutoloader for virtual module', async () => {
const scanResult = { dirs: [], files: [] }
mockScanCommandsDir.mockResolvedValueOnce(scanResult)
mockGenerateStaticAutoloader.mockReturnValueOnce('generated code')

const plugin = createAutoloadPlugin({
commandsDir: '/project/commands',
tagModulePath: '/project/tag.js',
})

const result = await plugin.load('\0virtual:kidd-static-commands')

mockReadFileSync.mockReturnValueOnce('const x = 1\n//#endregion\n')
expect(result).toBe('generated code')
expect(mockScanCommandsDir).toHaveBeenCalledOnce()
expect(mockGenerateStaticAutoloader).toHaveBeenCalledOnce()
})

it('should pass commandsDir and tagModulePath to generators', async () => {
const scanResult = { dirs: [], files: [] }
mockScanCommandsDir.mockResolvedValueOnce(scanResult)
mockGenerateStaticAutoloader.mockReturnValueOnce('generated code')

const result = transformCall[1]({
path: '/project/node_modules/@kidd-cli/core/dist/index.js',
const plugin = createAutoloadPlugin({
commandsDir: '/project/commands',
tagModulePath: '/project/tag.js',
})

expect(result).toBeUndefined()
await plugin.load('\0virtual:kidd-static-commands')

expect(mockScanCommandsDir).toHaveBeenCalledWith('/project/commands')
expect(mockGenerateStaticAutoloader).toHaveBeenCalledWith({
scan: scanResult,
tagModulePath: '/project/tag.js',
})
})
})
})
Loading
Loading