@docker-harpoon/monorepo 0.1.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.d.ts +69 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +122 -0
- package/dist/strategies/monorepo-simulation.d.ts +19 -0
- package/dist/strategies/monorepo-simulation.d.ts.map +1 -0
- package/dist/strategies/monorepo-simulation.js +144 -0
- package/dist/transformers/monorepo.d.ts +29 -0
- package/dist/transformers/monorepo.d.ts.map +1 -0
- package/dist/transformers/monorepo.js +92 -0
- package/package.json +27 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @harpoon/monorepo
|
|
3
|
+
*
|
|
4
|
+
* Package-manager agnostic monorepo binding for Harpoon.
|
|
5
|
+
*
|
|
6
|
+
* Provides:
|
|
7
|
+
* - MonorepoBinding: Build-time transformations for monorepo structure
|
|
8
|
+
* - Build strategy for simulating monorepo structure
|
|
9
|
+
* - Dockerfile transformers for monorepo paths
|
|
10
|
+
*
|
|
11
|
+
* Note: This package does NOT assume any specific package manager.
|
|
12
|
+
* Users should configure their own root files (lock files, workspace configs).
|
|
13
|
+
*/
|
|
14
|
+
import type { BuildBinding } from '@harpoon/core';
|
|
15
|
+
export { monorepoTransformers, monorepoRootFilesTransformer, monorepoCopyAllTransformer, monorepoDataPathTransformer, } from './transformers/monorepo';
|
|
16
|
+
export { monorepoSimulationStrategy } from './strategies/monorepo-simulation';
|
|
17
|
+
export interface MonorepoBindingOptions {
|
|
18
|
+
/** App name within the monorepo (e.g., "web", "api") */
|
|
19
|
+
appName: string;
|
|
20
|
+
/** Source directory for shared folders (default: context directory) */
|
|
21
|
+
source?: string;
|
|
22
|
+
/** Shared folders to copy (default: ['packages', 'tools']) */
|
|
23
|
+
sharedFolders?: readonly string[];
|
|
24
|
+
/** Root files to copy (default: ['package.json', 'pnpm-lock.yaml', 'pnpm-workspace.yaml', '.npmrc']) */
|
|
25
|
+
rootFiles?: readonly string[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Creates a Monorepo binding for build-time transformations.
|
|
29
|
+
*
|
|
30
|
+
* This binding provides:
|
|
31
|
+
* - Build context preparation (simulating monorepo structure)
|
|
32
|
+
* - Dockerfile transformations for monorepo paths
|
|
33
|
+
* - Config patching for pnpm workspaces
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* import { Image } from '@harpoon/core';
|
|
38
|
+
* import { MonorepoBinding } from '@harpoon/monorepo';
|
|
39
|
+
* import { NextjsBinding } from '@harpoon/nextjs';
|
|
40
|
+
*
|
|
41
|
+
* const appImage = await Image("my-app:latest", {
|
|
42
|
+
* context: "./apps/web",
|
|
43
|
+
* bindings: {
|
|
44
|
+
* MONOREPO: MonorepoBinding({
|
|
45
|
+
* appName: "web",
|
|
46
|
+
* source: "../..", // monorepo root
|
|
47
|
+
* }),
|
|
48
|
+
* NEXTJS: NextjsBinding({ standalone: true }),
|
|
49
|
+
* },
|
|
50
|
+
* });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function MonorepoBinding(options: MonorepoBindingOptions): BuildBinding<Record<string, never>>;
|
|
54
|
+
/**
|
|
55
|
+
* Register all monorepo plugins with Harpoon's registries.
|
|
56
|
+
*
|
|
57
|
+
* Call this once at application startup if you want the strategy,
|
|
58
|
+
* patchers, and transformers to be available globally.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* import { registerMonorepoPlugins } from '@harpoon/monorepo';
|
|
63
|
+
*
|
|
64
|
+
* registerMonorepoPlugins();
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function registerMonorepoPlugins(): void;
|
|
68
|
+
export default MonorepoBinding;
|
|
69
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EACV,YAAY,EAOb,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,oBAAoB,EACpB,4BAA4B,EAC5B,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAI9E,MAAM,WAAW,sBAAsB;IACrC,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,wGAAwG;IACxG,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC/B;AAID;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,GAC9B,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAoErC;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAgB9C;AAED,eAAe,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @harpoon/monorepo
|
|
3
|
+
*
|
|
4
|
+
* Package-manager agnostic monorepo binding for Harpoon.
|
|
5
|
+
*
|
|
6
|
+
* Provides:
|
|
7
|
+
* - MonorepoBinding: Build-time transformations for monorepo structure
|
|
8
|
+
* - Build strategy for simulating monorepo structure
|
|
9
|
+
* - Dockerfile transformers for monorepo paths
|
|
10
|
+
*
|
|
11
|
+
* Note: This package does NOT assume any specific package manager.
|
|
12
|
+
* Users should configure their own root files (lock files, workspace configs).
|
|
13
|
+
*/
|
|
14
|
+
import { Effect } from 'effect';
|
|
15
|
+
// Re-export transformers and strategies
|
|
16
|
+
export { monorepoTransformers, monorepoRootFilesTransformer, monorepoCopyAllTransformer, monorepoDataPathTransformer, } from './transformers/monorepo';
|
|
17
|
+
export { monorepoSimulationStrategy } from './strategies/monorepo-simulation';
|
|
18
|
+
// ============ Binding Implementation ============
|
|
19
|
+
/**
|
|
20
|
+
* Creates a Monorepo binding for build-time transformations.
|
|
21
|
+
*
|
|
22
|
+
* This binding provides:
|
|
23
|
+
* - Build context preparation (simulating monorepo structure)
|
|
24
|
+
* - Dockerfile transformations for monorepo paths
|
|
25
|
+
* - Config patching for pnpm workspaces
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* import { Image } from '@harpoon/core';
|
|
30
|
+
* import { MonorepoBinding } from '@harpoon/monorepo';
|
|
31
|
+
* import { NextjsBinding } from '@harpoon/nextjs';
|
|
32
|
+
*
|
|
33
|
+
* const appImage = await Image("my-app:latest", {
|
|
34
|
+
* context: "./apps/web",
|
|
35
|
+
* bindings: {
|
|
36
|
+
* MONOREPO: MonorepoBinding({
|
|
37
|
+
* appName: "web",
|
|
38
|
+
* source: "../..", // monorepo root
|
|
39
|
+
* }),
|
|
40
|
+
* NEXTJS: NextjsBinding({ standalone: true }),
|
|
41
|
+
* },
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export function MonorepoBinding(options) {
|
|
46
|
+
const { appName, source, sharedFolders, rootFiles } = options;
|
|
47
|
+
return {
|
|
48
|
+
type: 'monorepo',
|
|
49
|
+
resource: undefined,
|
|
50
|
+
getEnv() {
|
|
51
|
+
return {};
|
|
52
|
+
},
|
|
53
|
+
transformDockerfile(instructions, ctx) {
|
|
54
|
+
// Ensure context has appName
|
|
55
|
+
const monoCtx = {
|
|
56
|
+
...ctx,
|
|
57
|
+
appName: appName || ctx.appName,
|
|
58
|
+
};
|
|
59
|
+
// Import and apply monorepo transformers
|
|
60
|
+
const { monorepoTransformers } = require('./transformers/monorepo');
|
|
61
|
+
let result = [...instructions];
|
|
62
|
+
for (const transformer of monorepoTransformers) {
|
|
63
|
+
result = result.flatMap((inst) => {
|
|
64
|
+
if (!transformer.handlesTypes.includes(inst.type)) {
|
|
65
|
+
return [inst];
|
|
66
|
+
}
|
|
67
|
+
if (!transformer.matches(inst, monoCtx)) {
|
|
68
|
+
return [inst];
|
|
69
|
+
}
|
|
70
|
+
const transformed = transformer.transform(inst, monoCtx);
|
|
71
|
+
return Array.isArray(transformed) ? [...transformed] : [transformed];
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
},
|
|
76
|
+
prepareBuildContext(input, ctx) {
|
|
77
|
+
// If already in a temp directory with apps/, no need to simulate
|
|
78
|
+
if (ctx.contextPath.includes('temp-build')) {
|
|
79
|
+
return Effect.succeed(ctx);
|
|
80
|
+
}
|
|
81
|
+
// Use the monorepo simulation strategy
|
|
82
|
+
const { monorepoSimulationStrategy } = require('./strategies/monorepo-simulation');
|
|
83
|
+
const enhancedInput = {
|
|
84
|
+
...input,
|
|
85
|
+
monorepoAppName: appName,
|
|
86
|
+
...(source !== undefined && { monorepoSource: source }),
|
|
87
|
+
options: {
|
|
88
|
+
...input.options,
|
|
89
|
+
...(sharedFolders && { sharedFolders }),
|
|
90
|
+
...(rootFiles && { rootFiles }),
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
return monorepoSimulationStrategy.prepare(enhancedInput);
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// ============ Registration Helpers ============
|
|
98
|
+
/**
|
|
99
|
+
* Register all monorepo plugins with Harpoon's registries.
|
|
100
|
+
*
|
|
101
|
+
* Call this once at application startup if you want the strategy,
|
|
102
|
+
* patchers, and transformers to be available globally.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* import { registerMonorepoPlugins } from '@harpoon/monorepo';
|
|
107
|
+
*
|
|
108
|
+
* registerMonorepoPlugins();
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export function registerMonorepoPlugins() {
|
|
112
|
+
const core = require('@harpoon/core');
|
|
113
|
+
const { monorepoSimulationStrategy } = require('./strategies/monorepo-simulation');
|
|
114
|
+
const { monorepoTransformers } = require('./transformers/monorepo');
|
|
115
|
+
// Register build strategy
|
|
116
|
+
core.registerBuildStrategy(monorepoSimulationStrategy);
|
|
117
|
+
// Register transformers
|
|
118
|
+
for (const transformer of monorepoTransformers) {
|
|
119
|
+
core.registerTransformer(transformer);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
export default MonorepoBinding;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Monorepo Simulation Build Strategy
|
|
3
|
+
*
|
|
4
|
+
* Creates a simulated monorepo structure in a temp directory.
|
|
5
|
+
* Copies app content to apps/{appName}, shared folders, and root files.
|
|
6
|
+
*/
|
|
7
|
+
import type { BuildStrategy } from '@harpoon/core';
|
|
8
|
+
/**
|
|
9
|
+
* Monorepo simulation build strategy.
|
|
10
|
+
*
|
|
11
|
+
* Creates a temp directory with monorepo structure:
|
|
12
|
+
* - apps/{appName}/ - The app being built
|
|
13
|
+
* - packages/ - Shared packages
|
|
14
|
+
* - tools/ - Shared tools
|
|
15
|
+
* - Root files (package.json, pnpm-lock.yaml, etc.)
|
|
16
|
+
*/
|
|
17
|
+
export declare const monorepoSimulationStrategy: BuildStrategy;
|
|
18
|
+
export default monorepoSimulationStrategy;
|
|
19
|
+
//# sourceMappingURL=monorepo-simulation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monorepo-simulation.d.ts","sourceRoot":"","sources":["../../src/strategies/monorepo-simulation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAoC,MAAM,eAAe,CAAC;AAqGrF;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,EAAE,aAmGxC,CAAC;AAEF,eAAe,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Monorepo Simulation Build Strategy
|
|
3
|
+
*
|
|
4
|
+
* Creates a simulated monorepo structure in a temp directory.
|
|
5
|
+
* Copies app content to apps/{appName}, shared folders, and root files.
|
|
6
|
+
*/
|
|
7
|
+
import { Effect } from 'effect';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import * as fs from 'fs/promises';
|
|
10
|
+
import { BuildStrategyError } from '@harpoon/core';
|
|
11
|
+
// ============ Configuration ============
|
|
12
|
+
/**
|
|
13
|
+
* Default shared folders to copy from monorepo source.
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_SHARED_FOLDERS = ['packages', 'tools'];
|
|
16
|
+
/**
|
|
17
|
+
* Default root files to copy.
|
|
18
|
+
* Note: Intentionally minimal - users should specify their lock file.
|
|
19
|
+
*/
|
|
20
|
+
const DEFAULT_ROOT_FILES = ['package.json'];
|
|
21
|
+
// ============ Helper Functions ============
|
|
22
|
+
/**
|
|
23
|
+
* Recursively copy a directory.
|
|
24
|
+
*/
|
|
25
|
+
async function copyRecursive(src, dest) {
|
|
26
|
+
const proc = Bun.spawn(['cp', '-R', src, dest], {
|
|
27
|
+
stdout: 'ignore',
|
|
28
|
+
stderr: 'inherit',
|
|
29
|
+
});
|
|
30
|
+
const exitCode = await proc.exited;
|
|
31
|
+
if (exitCode !== 0) {
|
|
32
|
+
throw new Error(`Failed to copy ${src} to ${dest}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Copy shared folders to temp directory.
|
|
37
|
+
*/
|
|
38
|
+
async function copySharedFolders(sharedSource, tempDir, folders) {
|
|
39
|
+
const copiedFolders = [];
|
|
40
|
+
for (const folder of folders) {
|
|
41
|
+
const sourcePath = path.join(sharedSource, folder);
|
|
42
|
+
try {
|
|
43
|
+
await fs.access(sourcePath);
|
|
44
|
+
console.log(`[BuildStrategy:monorepo-simulation] Copying ${folder} from ${sharedSource}...`);
|
|
45
|
+
await copyRecursive(sourcePath, path.join(tempDir, folder));
|
|
46
|
+
copiedFolders.push(folder);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
console.warn(`[BuildStrategy:monorepo-simulation] Warning: ${folder} not found in ${sharedSource}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return Object.freeze(copiedFolders);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Copy root files to temp directory.
|
|
56
|
+
*/
|
|
57
|
+
async function copyRootFiles(context, sharedSource, tempDir, files) {
|
|
58
|
+
const copiedFiles = [];
|
|
59
|
+
for (const file of files) {
|
|
60
|
+
// Try app context first
|
|
61
|
+
let sourcePath = path.join(context, file);
|
|
62
|
+
try {
|
|
63
|
+
await fs.access(sourcePath);
|
|
64
|
+
await fs.copyFile(sourcePath, path.join(tempDir, file));
|
|
65
|
+
copiedFiles.push(file);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Fall through to try shared source
|
|
70
|
+
}
|
|
71
|
+
// Try shared source
|
|
72
|
+
sourcePath = path.join(sharedSource, file);
|
|
73
|
+
try {
|
|
74
|
+
await fs.access(sourcePath);
|
|
75
|
+
console.log(`[BuildStrategy:monorepo-simulation] Copying ${file} from ${sharedSource}...`);
|
|
76
|
+
await fs.copyFile(sourcePath, path.join(tempDir, file));
|
|
77
|
+
copiedFiles.push(file);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
console.warn(`[BuildStrategy:monorepo-simulation] Warning: ${file} not found in ${context} or ${sharedSource}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return Object.freeze(copiedFiles);
|
|
84
|
+
}
|
|
85
|
+
// ============ Strategy Implementation ============
|
|
86
|
+
/**
|
|
87
|
+
* Monorepo simulation build strategy.
|
|
88
|
+
*
|
|
89
|
+
* Creates a temp directory with monorepo structure:
|
|
90
|
+
* - apps/{appName}/ - The app being built
|
|
91
|
+
* - packages/ - Shared packages
|
|
92
|
+
* - tools/ - Shared tools
|
|
93
|
+
* - Root files (package.json, pnpm-lock.yaml, etc.)
|
|
94
|
+
*/
|
|
95
|
+
export const monorepoSimulationStrategy = {
|
|
96
|
+
name: 'monorepo-simulation',
|
|
97
|
+
description: 'Simulates monorepo structure in temp directory',
|
|
98
|
+
prepare: (input) => Effect.gen(function* () {
|
|
99
|
+
if (!input.monorepoAppName) {
|
|
100
|
+
yield* Effect.fail(new BuildStrategyError('monorepo-simulation', 'monorepoAppName is required for monorepo-simulation strategy'));
|
|
101
|
+
}
|
|
102
|
+
const appName = input.monorepoAppName;
|
|
103
|
+
const tempDir = path.resolve(process.cwd(), 'temp-build', input.tag.replace(':', '_'));
|
|
104
|
+
const sharedSource = input.monorepoSource ?? input.context;
|
|
105
|
+
// Get configurable options or use defaults
|
|
106
|
+
const sharedFolders = input.options?.sharedFolders ?? DEFAULT_SHARED_FOLDERS;
|
|
107
|
+
const rootFiles = input.options?.rootFiles ?? DEFAULT_ROOT_FILES;
|
|
108
|
+
// Clean and create temp directory
|
|
109
|
+
yield* Effect.tryPromise({
|
|
110
|
+
try: () => fs.rm(tempDir, { recursive: true, force: true }),
|
|
111
|
+
catch: (error) => new BuildStrategyError('monorepo-simulation', `Failed to clean temp directory: ${error}`, error instanceof Error ? error : undefined),
|
|
112
|
+
});
|
|
113
|
+
yield* Effect.tryPromise({
|
|
114
|
+
try: () => fs.mkdir(path.join(tempDir, 'apps'), { recursive: true }),
|
|
115
|
+
catch: (error) => new BuildStrategyError('monorepo-simulation', `Failed to create temp directory: ${error}`, error instanceof Error ? error : undefined),
|
|
116
|
+
});
|
|
117
|
+
// Copy app content
|
|
118
|
+
console.log(`[BuildStrategy:monorepo-simulation] Copying app content from ${input.context} to ${path.join(tempDir, 'apps', appName)}...`);
|
|
119
|
+
yield* Effect.tryPromise({
|
|
120
|
+
try: () => copyRecursive(input.context, path.join(tempDir, 'apps', appName)),
|
|
121
|
+
catch: (error) => new BuildStrategyError('monorepo-simulation', `Failed to copy app content: ${error}`, error instanceof Error ? error : undefined),
|
|
122
|
+
});
|
|
123
|
+
// Copy shared folders
|
|
124
|
+
yield* Effect.tryPromise({
|
|
125
|
+
try: () => copySharedFolders(sharedSource, tempDir, sharedFolders),
|
|
126
|
+
catch: (error) => new BuildStrategyError('monorepo-simulation', `Failed to copy shared folders: ${error}`, error instanceof Error ? error : undefined),
|
|
127
|
+
});
|
|
128
|
+
// Copy root files
|
|
129
|
+
yield* Effect.tryPromise({
|
|
130
|
+
try: () => copyRootFiles(input.context, sharedSource, tempDir, rootFiles),
|
|
131
|
+
catch: (error) => new BuildStrategyError('monorepo-simulation', `Failed to copy root files: ${error}`, error instanceof Error ? error : undefined),
|
|
132
|
+
});
|
|
133
|
+
const dockerfilePath = path.join(tempDir, 'apps', appName, 'Dockerfile');
|
|
134
|
+
console.log(`[BuildStrategy:monorepo-simulation] Simulating monorepo for ${input.tag} in ${tempDir}`);
|
|
135
|
+
const result = {
|
|
136
|
+
contextPath: tempDir,
|
|
137
|
+
dockerfilePath,
|
|
138
|
+
requiresCleanup: true,
|
|
139
|
+
tempDir,
|
|
140
|
+
};
|
|
141
|
+
return result;
|
|
142
|
+
}),
|
|
143
|
+
};
|
|
144
|
+
export default monorepoSimulationStrategy;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Monorepo Dockerfile Transformers
|
|
3
|
+
*
|
|
4
|
+
* Transforms for converting standalone Dockerfiles to monorepo structure.
|
|
5
|
+
* Handles COPY commands and build context transformations.
|
|
6
|
+
*/
|
|
7
|
+
import type { InstructionTransformer } from '@harpoon/core';
|
|
8
|
+
/**
|
|
9
|
+
* Transforms COPY commands for root package files.
|
|
10
|
+
* Matches when copying package.json with any lock file.
|
|
11
|
+
* Supports: pnpm-lock.yaml, package-lock.json, yarn.lock, bun.lockb
|
|
12
|
+
*/
|
|
13
|
+
export declare const monorepoRootFilesTransformer: InstructionTransformer;
|
|
14
|
+
/**
|
|
15
|
+
* Transforms COPY . . to copy individual folders.
|
|
16
|
+
*/
|
|
17
|
+
export declare const monorepoCopyAllTransformer: InstructionTransformer;
|
|
18
|
+
/**
|
|
19
|
+
* Skips database data paths (keeps them as-is).
|
|
20
|
+
* This is a "passthrough" transformer that prevents other transformers
|
|
21
|
+
* from modifying database paths.
|
|
22
|
+
*/
|
|
23
|
+
export declare const monorepoDataPathTransformer: InstructionTransformer;
|
|
24
|
+
/**
|
|
25
|
+
* All monorepo transformers for easy registration.
|
|
26
|
+
*/
|
|
27
|
+
export declare const monorepoTransformers: readonly InstructionTransformer[];
|
|
28
|
+
export default monorepoTransformers;
|
|
29
|
+
//# sourceMappingURL=monorepo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"monorepo.d.ts","sourceRoot":"","sources":["../../src/transformers/monorepo.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAA2C,MAAM,eAAe,CAAC;AAKrG;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,EAAE,sBA0B1C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B,EAAE,sBA4BxC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,EAAE,sBAiBzC,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,oBAAoB,EAAE,SAAS,sBAAsB,EAIhE,CAAC;AAEH,eAAe,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Monorepo Dockerfile Transformers
|
|
3
|
+
*
|
|
4
|
+
* Transforms for converting standalone Dockerfiles to monorepo structure.
|
|
5
|
+
* Handles COPY commands and build context transformations.
|
|
6
|
+
*/
|
|
7
|
+
import { replaceInstruction, splitInstruction } from '@harpoon/core';
|
|
8
|
+
// ============ Transformers ============
|
|
9
|
+
/**
|
|
10
|
+
* Transforms COPY commands for root package files.
|
|
11
|
+
* Matches when copying package.json with any lock file.
|
|
12
|
+
* Supports: pnpm-lock.yaml, package-lock.json, yarn.lock, bun.lockb
|
|
13
|
+
*/
|
|
14
|
+
export const monorepoRootFilesTransformer = {
|
|
15
|
+
name: 'monorepo-root-files',
|
|
16
|
+
description: 'Transforms COPY for root package files in monorepo',
|
|
17
|
+
handlesTypes: ['COPY'],
|
|
18
|
+
matches: (inst, _ctx) => {
|
|
19
|
+
const arg = inst.args[0] || '';
|
|
20
|
+
// Must have package.json
|
|
21
|
+
if (!arg.includes('package.json'))
|
|
22
|
+
return false;
|
|
23
|
+
// Must have some lock file (any package manager)
|
|
24
|
+
return (arg.includes('pnpm-lock.yaml') ||
|
|
25
|
+
arg.includes('package-lock.json') ||
|
|
26
|
+
arg.includes('yarn.lock') ||
|
|
27
|
+
arg.includes('bun.lockb'));
|
|
28
|
+
},
|
|
29
|
+
transform: (inst, ctx) => {
|
|
30
|
+
const rootFiles = ctx.copiedRootFiles.join(' ');
|
|
31
|
+
return replaceInstruction(inst, `COPY ${rootFiles} ./`, Object.freeze([rootFiles, './']));
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Transforms COPY . . to copy individual folders.
|
|
36
|
+
*/
|
|
37
|
+
export const monorepoCopyAllTransformer = {
|
|
38
|
+
name: 'monorepo-copy-all',
|
|
39
|
+
description: 'Transforms COPY . . to copy individual folders for monorepo',
|
|
40
|
+
handlesTypes: ['COPY'],
|
|
41
|
+
matches: (inst, _ctx) => {
|
|
42
|
+
return inst.args[0] === '.' && inst.args[1] === '.';
|
|
43
|
+
},
|
|
44
|
+
transform: (inst, ctx) => {
|
|
45
|
+
const replacements = [];
|
|
46
|
+
// Add COPY for each shared folder
|
|
47
|
+
for (const folder of ctx.copiedFolders) {
|
|
48
|
+
replacements.push({
|
|
49
|
+
original: `COPY ${folder} ./${folder}`,
|
|
50
|
+
args: Object.freeze([folder, `./${folder}`]),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
// Add COPY for the app folder
|
|
54
|
+
replacements.push({
|
|
55
|
+
original: `COPY apps/${ctx.appName} ./apps/${ctx.appName}`,
|
|
56
|
+
args: Object.freeze([`apps/${ctx.appName}`, `./apps/${ctx.appName}`]),
|
|
57
|
+
});
|
|
58
|
+
return splitInstruction(inst, replacements);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Skips database data paths (keeps them as-is).
|
|
63
|
+
* This is a "passthrough" transformer that prevents other transformers
|
|
64
|
+
* from modifying database paths.
|
|
65
|
+
*/
|
|
66
|
+
export const monorepoDataPathTransformer = {
|
|
67
|
+
name: 'monorepo-data-path',
|
|
68
|
+
description: 'Preserves /app/data/ paths without transformation',
|
|
69
|
+
handlesTypes: ['COPY'],
|
|
70
|
+
matches: (inst, ctx) => {
|
|
71
|
+
if (!ctx.currentStage?.toLowerCase().includes('runner'))
|
|
72
|
+
return false;
|
|
73
|
+
if (!inst.args[0]?.includes('--from=builder'))
|
|
74
|
+
return false;
|
|
75
|
+
const sourcePath = inst.args[1];
|
|
76
|
+
return sourcePath?.includes('/app/data/') || false;
|
|
77
|
+
},
|
|
78
|
+
transform: (inst, _ctx) => {
|
|
79
|
+
// Return unchanged - this prevents other transformers from modifying it
|
|
80
|
+
return inst;
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
// ============ Exports ============
|
|
84
|
+
/**
|
|
85
|
+
* All monorepo transformers for easy registration.
|
|
86
|
+
*/
|
|
87
|
+
export const monorepoTransformers = Object.freeze([
|
|
88
|
+
monorepoRootFilesTransformer,
|
|
89
|
+
monorepoCopyAllTransformer,
|
|
90
|
+
monorepoDataPathTransformer,
|
|
91
|
+
]);
|
|
92
|
+
export default monorepoTransformers;
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@docker-harpoon/monorepo",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Monorepo binding for Harpoon - package-manager agnostic build context simulation",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": ["dist"],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"typecheck": "tsc --noEmit"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"effect": "^3.19.14"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@docker-harpoon/core": ">=0.1.0",
|
|
25
|
+
"bun": ">=1.0.0"
|
|
26
|
+
}
|
|
27
|
+
}
|