@docker-harpoon/nextjs 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.
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @harpoon/nextjs
3
+ *
4
+ * Next.js binding for Harpoon.
5
+ *
6
+ * Provides:
7
+ * - NextjsBinding: Build-time transformations for Next.js standalone output
8
+ * - Dockerfile transformers for .next/standalone paths
9
+ */
10
+ import type { BuildBinding } from '@harpoon/core';
11
+ export { nextjsTransformers, nextjsPublicTransformer, nextjsStaticTransformer, nextjsStandaloneTransformer, nextjsCmdTransformer, } from './transformers/nextjs';
12
+ export interface NextjsBindingOptions {
13
+ /** Enable standalone output transformations (default: true) */
14
+ standalone?: boolean;
15
+ /** Custom output directory (default: .next) */
16
+ outputDir?: string;
17
+ }
18
+ /**
19
+ * Creates a Next.js binding for build-time Dockerfile transformations.
20
+ *
21
+ * This binding transforms Dockerfile instructions to work with Next.js
22
+ * standalone output in a monorepo structure.
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import { Image } from '@harpoon/core';
27
+ * import { NextjsBinding } from '@harpoon/nextjs';
28
+ * import { MonorepoBinding } from '@harpoon/monorepo';
29
+ *
30
+ * // Build image with Next.js transformations
31
+ * const appImage = await Image("my-nextjs-app:latest", {
32
+ * context: "./app",
33
+ * bindings: {
34
+ * NEXTJS: NextjsBinding({ standalone: true }),
35
+ * MONOREPO: MonorepoBinding({ appName: "web" }),
36
+ * },
37
+ * });
38
+ * ```
39
+ */
40
+ export declare function NextjsBinding(options?: NextjsBindingOptions): BuildBinding<Record<string, never>>;
41
+ /**
42
+ * Register all Next.js transformers with Harpoon's registry.
43
+ *
44
+ * Call this once at application startup if you want the transformers
45
+ * to be available globally.
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * import { registerNextjsPlugins } from '@harpoon/nextjs';
50
+ *
51
+ * registerNextjsPlugins();
52
+ * ```
53
+ */
54
+ export declare function registerNextjsPlugins(): void;
55
+ export default NextjsBinding;
56
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,YAAY,EAIb,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,uBAAuB,EACvB,2BAA2B,EAC3B,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAI/B,MAAM,WAAW,oBAAoB;IACnC,+DAA+D;IAC/D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,aAAa,CAC3B,OAAO,GAAE,oBAAyB,GACjC,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CA8CrC;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAU5C;AAED,eAAe,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,92 @@
1
+ /**
2
+ * @harpoon/nextjs
3
+ *
4
+ * Next.js binding for Harpoon.
5
+ *
6
+ * Provides:
7
+ * - NextjsBinding: Build-time transformations for Next.js standalone output
8
+ * - Dockerfile transformers for .next/standalone paths
9
+ */
10
+ // Re-export transformers
11
+ export { nextjsTransformers, nextjsPublicTransformer, nextjsStaticTransformer, nextjsStandaloneTransformer, nextjsCmdTransformer, } from './transformers/nextjs';
12
+ // ============ Binding Implementation ============
13
+ /**
14
+ * Creates a Next.js binding for build-time Dockerfile transformations.
15
+ *
16
+ * This binding transforms Dockerfile instructions to work with Next.js
17
+ * standalone output in a monorepo structure.
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { Image } from '@harpoon/core';
22
+ * import { NextjsBinding } from '@harpoon/nextjs';
23
+ * import { MonorepoBinding } from '@harpoon/monorepo';
24
+ *
25
+ * // Build image with Next.js transformations
26
+ * const appImage = await Image("my-nextjs-app:latest", {
27
+ * context: "./app",
28
+ * bindings: {
29
+ * NEXTJS: NextjsBinding({ standalone: true }),
30
+ * MONOREPO: MonorepoBinding({ appName: "web" }),
31
+ * },
32
+ * });
33
+ * ```
34
+ */
35
+ export function NextjsBinding(options = {}) {
36
+ const standalone = options.standalone !== false;
37
+ return {
38
+ type: 'nextjs',
39
+ resource: undefined,
40
+ getEnv() {
41
+ return {};
42
+ },
43
+ transformDockerfile(instructions, ctx) {
44
+ if (!standalone) {
45
+ return instructions;
46
+ }
47
+ // Mark context as standalone app for transformers
48
+ const standaloneCtx = {
49
+ ...ctx,
50
+ isStandaloneApp: true,
51
+ };
52
+ // Import and apply nextjs transformers
53
+ const { nextjsTransformers } = require('./transformers/nextjs');
54
+ let result = [...instructions];
55
+ for (const transformer of nextjsTransformers) {
56
+ result = result.flatMap((inst) => {
57
+ if (!transformer.handlesTypes.includes(inst.type)) {
58
+ return [inst];
59
+ }
60
+ if (!transformer.matches(inst, standaloneCtx)) {
61
+ return [inst];
62
+ }
63
+ const transformed = transformer.transform(inst, standaloneCtx);
64
+ return Array.isArray(transformed) ? [...transformed] : [transformed];
65
+ });
66
+ }
67
+ return result;
68
+ },
69
+ };
70
+ }
71
+ // ============ Registration Helpers ============
72
+ /**
73
+ * Register all Next.js transformers with Harpoon's registry.
74
+ *
75
+ * Call this once at application startup if you want the transformers
76
+ * to be available globally.
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * import { registerNextjsPlugins } from '@harpoon/nextjs';
81
+ *
82
+ * registerNextjsPlugins();
83
+ * ```
84
+ */
85
+ export function registerNextjsPlugins() {
86
+ const core = require('@harpoon/core');
87
+ const { nextjsTransformers } = require('./transformers/nextjs');
88
+ for (const transformer of nextjsTransformers) {
89
+ core.registerTransformer(transformer);
90
+ }
91
+ }
92
+ export default NextjsBinding;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Next.js Dockerfile Transformers
3
+ *
4
+ * Transforms for Next.js standalone output paths in Dockerfiles.
5
+ * Handles .next/standalone, .next/static, and public folder paths.
6
+ */
7
+ import type { InstructionTransformer } from '@harpoon/core';
8
+ /**
9
+ * Transforms COPY --from=builder for /app/public paths.
10
+ */
11
+ export declare const nextjsPublicTransformer: InstructionTransformer;
12
+ /**
13
+ * Transforms COPY --from=builder for /app/.next/static paths.
14
+ */
15
+ export declare const nextjsStaticTransformer: InstructionTransformer;
16
+ /**
17
+ * Transforms COPY --from=builder for /app/.next/standalone paths.
18
+ */
19
+ export declare const nextjsStandaloneTransformer: InstructionTransformer;
20
+ /**
21
+ * Transforms CMD to use monorepo path for server.js.
22
+ */
23
+ export declare const nextjsCmdTransformer: InstructionTransformer;
24
+ /**
25
+ * All Next.js transformers for easy registration.
26
+ */
27
+ export declare const nextjsTransformers: readonly InstructionTransformer[];
28
+ export default nextjsTransformers;
29
+ //# sourceMappingURL=nextjs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nextjs.d.ts","sourceRoot":"","sources":["../../src/transformers/nextjs.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAA2C,MAAM,eAAe,CAAC;AAqBrG;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAE,sBAqCrC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAE,sBAqCrC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,2BAA2B,EAAE,sBAqCzC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,EAAE,sBAiBlC,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,sBAAsB,EAK9D,CAAC;AAEH,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Next.js Dockerfile Transformers
3
+ *
4
+ * Transforms for Next.js standalone output paths in Dockerfiles.
5
+ * Handles .next/standalone, .next/static, and public folder paths.
6
+ */
7
+ import { replaceInstruction } from '@harpoon/core';
8
+ // ============ Helper Functions ============
9
+ /**
10
+ * Check if this is a standalone Next.js app (not already monorepo structure).
11
+ */
12
+ function isStandaloneApp(ctx) {
13
+ return ctx.isStandaloneApp === true;
14
+ }
15
+ /**
16
+ * Check if we're in the runner stage.
17
+ */
18
+ function isRunnerStage(ctx) {
19
+ return ctx.currentStage?.toLowerCase() === 'runner';
20
+ }
21
+ // ============ Transformers ============
22
+ /**
23
+ * Transforms COPY --from=builder for /app/public paths.
24
+ */
25
+ export const nextjsPublicTransformer = {
26
+ name: 'nextjs-public',
27
+ description: 'Transforms /app/public paths for Next.js monorepo structure',
28
+ handlesTypes: ['COPY'],
29
+ matches: (inst, ctx) => {
30
+ if (!isStandaloneApp(ctx) || !isRunnerStage(ctx))
31
+ return false;
32
+ if (!inst.args[0]?.includes('--from=builder'))
33
+ return false;
34
+ const sourcePath = inst.args[1];
35
+ return !!(sourcePath?.includes('/app/public') &&
36
+ !sourcePath.includes('/apps/'));
37
+ },
38
+ transform: (inst, ctx) => {
39
+ const sourcePath = inst.args[1] || '';
40
+ const destPath = inst.args[2] || '';
41
+ const newSource = sourcePath.replace('/app/public', `/app/apps/${ctx.appName}/public`);
42
+ const newDest = destPath.replace('./public', `./apps/${ctx.appName}/public`);
43
+ return replaceInstruction(inst, inst.original
44
+ .replace('/app/public', `/app/apps/${ctx.appName}/public`)
45
+ .replace('./public', `./apps/${ctx.appName}/public`), Object.freeze([inst.args[0] || '', newSource, newDest]));
46
+ },
47
+ };
48
+ /**
49
+ * Transforms COPY --from=builder for /app/.next/static paths.
50
+ */
51
+ export const nextjsStaticTransformer = {
52
+ name: 'nextjs-static',
53
+ description: 'Transforms .next/static paths for Next.js monorepo structure',
54
+ handlesTypes: ['COPY'],
55
+ matches: (inst, ctx) => {
56
+ if (!isStandaloneApp(ctx) || !isRunnerStage(ctx))
57
+ return false;
58
+ if (!inst.args[0]?.includes('--from=builder'))
59
+ return false;
60
+ const sourcePath = inst.args[1];
61
+ return !!(sourcePath?.includes('/app/.next/static') &&
62
+ !sourcePath.includes('/apps/'));
63
+ },
64
+ transform: (inst, ctx) => {
65
+ const sourcePath = inst.args[1] || '';
66
+ const destPath = inst.args[2] || '';
67
+ const newSource = sourcePath.replace('/app/.next/static', `/app/apps/${ctx.appName}/.next/static`);
68
+ const newDest = destPath.replace('./.next/static', `./apps/${ctx.appName}/.next/static`);
69
+ return replaceInstruction(inst, inst.original
70
+ .replace('/app/.next/static', `/app/apps/${ctx.appName}/.next/static`)
71
+ .replace('./.next/static', `./apps/${ctx.appName}/.next/static`), Object.freeze([inst.args[0] || '', newSource, newDest]));
72
+ },
73
+ };
74
+ /**
75
+ * Transforms COPY --from=builder for /app/.next/standalone paths.
76
+ */
77
+ export const nextjsStandaloneTransformer = {
78
+ name: 'nextjs-standalone',
79
+ description: 'Transforms .next/standalone paths for Next.js monorepo structure',
80
+ handlesTypes: ['COPY'],
81
+ matches: (inst, ctx) => {
82
+ if (!isStandaloneApp(ctx) || !isRunnerStage(ctx))
83
+ return false;
84
+ if (!inst.args[0]?.includes('--from=builder'))
85
+ return false;
86
+ const sourcePath = inst.args[1];
87
+ return !!(sourcePath?.includes('/app/.next/standalone') &&
88
+ !sourcePath.includes('/apps/'));
89
+ },
90
+ transform: (inst, ctx) => {
91
+ const sourcePath = inst.args[1] || '';
92
+ const newSource = sourcePath.replace('/app/.next/standalone', `/app/apps/${ctx.appName}/.next/standalone`);
93
+ return replaceInstruction(inst, inst.original.replace('/app/.next/standalone', `/app/apps/${ctx.appName}/.next/standalone`), Object.freeze([
94
+ inst.args[0] || '',
95
+ newSource,
96
+ './', // Keep dest as './' - standalone structure will have apps/{appName}/server.js
97
+ ]));
98
+ },
99
+ };
100
+ /**
101
+ * Transforms CMD to use monorepo path for server.js.
102
+ */
103
+ export const nextjsCmdTransformer = {
104
+ name: 'nextjs-cmd',
105
+ description: 'Transforms CMD to use monorepo path for server.js',
106
+ handlesTypes: ['CMD'],
107
+ matches: (inst, ctx) => {
108
+ if (!isStandaloneApp(ctx))
109
+ return false;
110
+ return inst.original.includes('server.js');
111
+ },
112
+ transform: (inst, ctx) => {
113
+ return replaceInstruction(inst, `CMD ["node", "apps/${ctx.appName}/server.js"]`, Object.freeze(['node', `apps/${ctx.appName}/server.js`]));
114
+ },
115
+ };
116
+ // ============ Exports ============
117
+ /**
118
+ * All Next.js transformers for easy registration.
119
+ */
120
+ export const nextjsTransformers = Object.freeze([
121
+ nextjsPublicTransformer,
122
+ nextjsStaticTransformer,
123
+ nextjsStandaloneTransformer,
124
+ nextjsCmdTransformer,
125
+ ]);
126
+ export default nextjsTransformers;
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@docker-harpoon/nextjs",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "description": "Next.js binding for Harpoon - standalone output Dockerfile transformations",
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
+ "@docker-harpoon/core": "workspace:*"
22
+ },
23
+ "peerDependencies": {
24
+ "bun": ">=1.0.0"
25
+ }
26
+ }