@devup-api/next-plugin 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/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # @devup-api/next-plugin
2
+
3
+ devup API plugin for Next.js
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @devup-api/next-plugin
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import devupApiPlugin from '@devup-api/next-plugin';
15
+
16
+ const nextConfig = {
17
+ // ... existing configuration
18
+ ...devupApiPlugin({
19
+ // options
20
+ }),
21
+ };
22
+ ```
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=plugin.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/plugin.test.ts"],"names":[],"mappings":""}
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ var{defineProperty:G,getOwnPropertyNames:Q,getOwnPropertyDescriptor:R}=Object,S=Object.prototype.hasOwnProperty;var H=new WeakMap,V=(x)=>{var q=H.get(x),z;if(q)return q;if(q=G({},"__esModule",{value:!0}),x&&typeof x==="object"||typeof x==="function")Q(x).map((y)=>!S.call(q,y)&&G(q,y,{get:()=>x[y],enumerable:!(z=R(x,y))||z.enumerable}));return H.set(x,q),q};var X=(x,q)=>{for(var z in q)G(x,z,{get:q[z],enumerable:!0,configurable:!0,set:(y)=>q[z]=()=>y})};var Y={};X(Y,{devupApi:()=>L,default:()=>L});module.exports=V(Y);var J=require("node:path"),F=require("@devup-api/generator"),A=require("@devup-api/utils"),K=require("@devup-api/webpack-plugin");function L(x,q={}){if(process.env.TURBOPACK==="1"||process.env.TURBOPACK==="auto"){let B=A.createTmpDir(q?.tempDir),E=A.readOpenapi(q?.openapiFile);A.writeInterface(J.join(B,"api.d.ts"),F.generateInterface(E,q));let N=F.createUrlMap(E,q);return x.env??={},Object.assign(x.env,{DEVUP_API_URL_MAP:JSON.stringify(N)}),x}let y=x.webpack;return x.webpack=(B,E)=>{if(B.plugins.push(new K.devupApiWebpackPlugin(q)),typeof y==="function")return y(B,E);return B},x}
@@ -0,0 +1,3 @@
1
+ export * from './plugin';
2
+ export { devupApi as default } from './plugin';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAA;AACxB,OAAO,EAAE,QAAQ,IAAI,OAAO,EAAE,MAAM,UAAU,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import{join as E}from"node:path";import{createUrlMap as F,generateInterface as G}from"@devup-api/generator";import{createTmpDir as H,readOpenapi as J,writeInterface as K}from"@devup-api/utils";import{devupApiWebpackPlugin as L}from"@devup-api/webpack-plugin";function N(x,q={}){if(process.env.TURBOPACK==="1"||process.env.TURBOPACK==="auto"){let y=H(q?.tempDir),z=J(q?.openapiFile);K(E(y,"api.d.ts"),G(z,q));let B=F(z,q);return x.env??={},Object.assign(x.env,{DEVUP_API_URL_MAP:JSON.stringify(B)}),x}let A=x.webpack;return x.webpack=(y,z)=>{if(y.plugins.push(new L(q)),typeof A==="function")return A(y,z);return y},x}export{N as devupApi,N as default};
@@ -0,0 +1,4 @@
1
+ import type { DevupApiOptions } from '@devup-api/core';
2
+ import type { NextConfig } from 'next';
3
+ export declare function devupApi(config: NextConfig, options?: DevupApiOptions): NextConfig;
4
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAItD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAA;AAEtC,wBAAgB,QAAQ,CACtB,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,eAAoB,GAC5B,UAAU,CA2BZ"}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@devup-api/next-plugin",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "import": "./dist/index.js",
8
+ "require": "./dist/index.cjs",
9
+ "types": "./dist/index.d.ts"
10
+ }
11
+ },
12
+ "scripts": {
13
+ "build": "tsc && bun build --target node --outfile=dist/index.js src/index.ts --production --packages=external && bun build --target node --outfile=dist/index.cjs --format=cjs src/index.ts --production --packages=external"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "dependencies": {
19
+ "@devup-api/utils": "0.1.0",
20
+ "@devup-api/core": "0.1.0",
21
+ "@devup-api/generator": "0.1.0",
22
+ "@devup-api/webpack-plugin": "0.1.0"
23
+ },
24
+ "peerDependencies": {
25
+ "next": "*",
26
+ "@devup-api/core": "*"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^24.10",
30
+ "@types/webpack": "^5.28",
31
+ "typescript": "^5.9"
32
+ }
33
+ }
@@ -0,0 +1,9 @@
1
+ import { expect, test } from 'bun:test'
2
+ import * as indexModule from '../index'
3
+
4
+ test('index.ts exports', () => {
5
+ expect({ ...indexModule }).toEqual({
6
+ devupApi: expect.any(Function),
7
+ default: expect.any(Function),
8
+ })
9
+ })
@@ -0,0 +1,286 @@
1
+ import { beforeEach, expect, mock, spyOn, test } from 'bun:test'
2
+ import { join } from 'node:path'
3
+ import type { DevupApiOptions } from '@devup-api/core'
4
+ import * as generator from '@devup-api/generator'
5
+ import * as utils from '@devup-api/utils'
6
+ import { devupApiWebpackPlugin } from '@devup-api/webpack-plugin'
7
+ import type { NextConfig } from 'next'
8
+ import { devupApi } from '../plugin'
9
+
10
+ let mockCreateTmpDir: ReturnType<typeof spyOn>
11
+ let mockReadOpenapi: ReturnType<typeof spyOn>
12
+ let mockWriteInterface: ReturnType<typeof spyOn>
13
+ let mockCreateTmpDirAsync: ReturnType<typeof spyOn>
14
+ let mockReadOpenapiAsync: ReturnType<typeof spyOn>
15
+ let mockWriteInterfaceAsync: ReturnType<typeof spyOn>
16
+ let mockCreateUrlMap: ReturnType<typeof spyOn>
17
+ let mockGenerateInterface: ReturnType<typeof spyOn>
18
+
19
+ const mockSchema = {
20
+ openapi: '3.1.0',
21
+ paths: {
22
+ '/users': {
23
+ get: {
24
+ operationId: 'getUsers',
25
+ responses: {
26
+ '200': {
27
+ content: {
28
+ 'application/json': {
29
+ schema: {
30
+ type: 'array',
31
+ items: { type: 'string' },
32
+ },
33
+ },
34
+ },
35
+ },
36
+ },
37
+ },
38
+ },
39
+ },
40
+ } as const
41
+
42
+ const mockUrlMap = {
43
+ getUsers: {
44
+ method: 'GET' as const,
45
+ url: '/users',
46
+ },
47
+ '/users': {
48
+ method: 'GET' as const,
49
+ url: '/users',
50
+ },
51
+ }
52
+
53
+ const mockInterfaceContent = 'export interface Test {}'
54
+
55
+ beforeEach(() => {
56
+ mockCreateTmpDir = spyOn(utils, 'createTmpDir').mockReturnValue('df')
57
+ mockReadOpenapi = spyOn(utils, 'readOpenapi').mockReturnValue(
58
+ mockSchema as never,
59
+ )
60
+ mockWriteInterface = spyOn(utils, 'writeInterface').mockImplementation(
61
+ () => {},
62
+ )
63
+ mockCreateTmpDirAsync = spyOn(utils, 'createTmpDirAsync').mockResolvedValue(
64
+ 'df',
65
+ )
66
+ mockReadOpenapiAsync = spyOn(utils, 'readOpenapiAsync').mockResolvedValue(
67
+ mockSchema as never,
68
+ )
69
+ mockWriteInterfaceAsync = spyOn(
70
+ utils,
71
+ 'writeInterfaceAsync',
72
+ ).mockResolvedValue(undefined)
73
+ mockCreateUrlMap = spyOn(generator, 'createUrlMap').mockReturnValue(
74
+ mockUrlMap as never,
75
+ )
76
+ mockGenerateInterface = spyOn(generator, 'generateInterface').mockReturnValue(
77
+ mockInterfaceContent,
78
+ )
79
+ mockCreateTmpDir.mockClear()
80
+ mockReadOpenapi.mockClear()
81
+ mockWriteInterface.mockClear()
82
+ mockCreateTmpDirAsync.mockClear()
83
+ mockReadOpenapiAsync.mockClear()
84
+ mockWriteInterfaceAsync.mockClear()
85
+ mockCreateUrlMap.mockClear()
86
+ mockGenerateInterface.mockClear()
87
+ })
88
+
89
+ test.each([
90
+ [{}, undefined],
91
+ [{ env: {} }, undefined],
92
+ [{}, { tempDir: 'custom-dir' }],
93
+ [{ env: {} }, { openapiFile: 'custom-openapi.json' }],
94
+ ] as const)('devupApi handles turbo mode: config=%s, options=%s', (config: NextConfig, options:
95
+ | DevupApiOptions
96
+ | undefined) => {
97
+ const originalEnv = process.env.TURBOPACK
98
+ process.env.TURBOPACK = '1'
99
+
100
+ try {
101
+ const result = devupApi(config, options)
102
+
103
+ expect(mockCreateTmpDir).toHaveBeenCalledWith(options?.tempDir)
104
+ expect(mockReadOpenapi).toHaveBeenCalledWith(options?.openapiFile)
105
+ expect(mockGenerateInterface).toHaveBeenCalledWith(
106
+ mockSchema,
107
+ options || {},
108
+ )
109
+ expect(mockWriteInterface).toHaveBeenCalledWith(
110
+ join('df', 'api.d.ts'),
111
+ mockInterfaceContent,
112
+ )
113
+ expect(mockCreateUrlMap).toHaveBeenCalledWith(mockSchema, options || {})
114
+ expect(result.env).toEqual({
115
+ DEVUP_API_URL_MAP: JSON.stringify(mockUrlMap),
116
+ })
117
+ expect(result).toBe(config)
118
+ } finally {
119
+ process.env.TURBOPACK = originalEnv
120
+ }
121
+ })
122
+
123
+ test('devupApi handles turbo mode with existing env', () => {
124
+ const originalEnv = process.env.TURBOPACK
125
+ process.env.TURBOPACK = '1'
126
+
127
+ try {
128
+ const config: NextConfig = {
129
+ env: {
130
+ EXISTING_VAR: 'value',
131
+ },
132
+ }
133
+ const result = devupApi(config)
134
+
135
+ expect(result.env).toEqual({
136
+ EXISTING_VAR: 'value',
137
+ DEVUP_API_URL_MAP: JSON.stringify(mockUrlMap),
138
+ })
139
+ } finally {
140
+ process.env.TURBOPACK = originalEnv
141
+ }
142
+ })
143
+
144
+ test('devupApi handles turbo mode with TURBOPACK=auto', () => {
145
+ const originalEnv = process.env.TURBOPACK
146
+ process.env.TURBOPACK = 'auto'
147
+
148
+ try {
149
+ const config: NextConfig = {}
150
+ const result = devupApi(config)
151
+
152
+ expect(mockCreateTmpDir).toHaveBeenCalled()
153
+ expect(result.env).toEqual({
154
+ DEVUP_API_URL_MAP: JSON.stringify(mockUrlMap),
155
+ })
156
+ } finally {
157
+ process.env.TURBOPACK = originalEnv
158
+ }
159
+ })
160
+
161
+ test.each([
162
+ [{}, undefined],
163
+ [{ webpack: undefined }, undefined],
164
+ [{}, { tempDir: 'custom-dir' }],
165
+ [{ webpack: undefined }, { openapiFile: 'custom-openapi.json' }],
166
+ ] as const)('devupApi handles webpack mode: config=%s, options=%s', (config: NextConfig, options:
167
+ | DevupApiOptions
168
+ | undefined) => {
169
+ const originalEnv = process.env.TURBOPACK
170
+ delete process.env.TURBOPACK
171
+
172
+ try {
173
+ const result = devupApi(config, options)
174
+
175
+ expect(result.webpack).toBeDefined()
176
+ expect(typeof result.webpack).toBe('function')
177
+ expect(result).toBe(config)
178
+ } finally {
179
+ process.env.TURBOPACK = originalEnv
180
+ }
181
+ })
182
+
183
+ test('devupApi handles webpack mode with existing webpack function', () => {
184
+ const originalEnv = process.env.TURBOPACK
185
+ delete process.env.TURBOPACK
186
+
187
+ try {
188
+ const existingWebpack = mock(() => ({}))
189
+ const config: NextConfig = {
190
+ webpack: existingWebpack as never,
191
+ }
192
+ const result = devupApi(config)
193
+
194
+ expect(result.webpack).toBeDefined()
195
+ expect(typeof result.webpack).toBe('function')
196
+ expect(result.webpack).not.toBe(existingWebpack)
197
+ } finally {
198
+ process.env.TURBOPACK = originalEnv
199
+ }
200
+ })
201
+
202
+ test('devupApi webpack function adds plugin to config', () => {
203
+ const originalEnv = process.env.TURBOPACK
204
+ delete process.env.TURBOPACK
205
+
206
+ try {
207
+ const config: NextConfig = {}
208
+ const result = devupApi(config)
209
+
210
+ const webpackConfig = {
211
+ plugins: [],
212
+ }
213
+ const webpackOptions = {}
214
+ const webpackResult = result.webpack?.(
215
+ webpackConfig as never,
216
+ webpackOptions as never,
217
+ )
218
+
219
+ expect(webpackConfig.plugins).toHaveLength(1)
220
+ expect(webpackConfig.plugins[0]).toBeInstanceOf(devupApiWebpackPlugin)
221
+ expect(webpackResult).toBe(webpackConfig)
222
+ } finally {
223
+ process.env.TURBOPACK = originalEnv
224
+ }
225
+ })
226
+
227
+ test('devupApi webpack function calls existing webpack function', () => {
228
+ const originalEnv = process.env.TURBOPACK
229
+ delete process.env.TURBOPACK
230
+
231
+ try {
232
+ const existingWebpack = mock(() => ({ modified: true }))
233
+ const config: NextConfig = {
234
+ webpack: existingWebpack as never,
235
+ }
236
+ const result = devupApi(config)
237
+
238
+ const webpackConfig = {
239
+ plugins: [],
240
+ }
241
+ const webpackOptions = {}
242
+ const webpackResult = result.webpack?.(
243
+ webpackConfig as never,
244
+ webpackOptions as never,
245
+ )
246
+
247
+ expect(existingWebpack).toHaveBeenCalledWith(webpackConfig, webpackOptions)
248
+ expect(webpackResult).toEqual({ modified: true })
249
+ } finally {
250
+ process.env.TURBOPACK = originalEnv
251
+ }
252
+ })
253
+
254
+ test('devupApi handles null urlMap in turbo mode', () => {
255
+ const originalEnv = process.env.TURBOPACK
256
+ process.env.TURBOPACK = '1'
257
+ mockCreateUrlMap.mockReturnValueOnce(null as never)
258
+
259
+ try {
260
+ const config: NextConfig = {}
261
+ const result = devupApi(config)
262
+
263
+ expect(result.env).toEqual({
264
+ DEVUP_API_URL_MAP: JSON.stringify(null),
265
+ })
266
+ } finally {
267
+ process.env.TURBOPACK = originalEnv
268
+ }
269
+ })
270
+
271
+ test('devupApi handles undefined urlMap in turbo mode', () => {
272
+ const originalEnv = process.env.TURBOPACK
273
+ process.env.TURBOPACK = '1'
274
+ mockCreateUrlMap.mockReturnValueOnce(undefined as never)
275
+
276
+ try {
277
+ const config: NextConfig = {}
278
+ const result = devupApi(config)
279
+
280
+ expect(result.env).toEqual({
281
+ DEVUP_API_URL_MAP: JSON.stringify(undefined),
282
+ })
283
+ } finally {
284
+ process.env.TURBOPACK = originalEnv
285
+ }
286
+ })
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './plugin'
2
+ export { devupApi as default } from './plugin'
package/src/plugin.ts ADDED
@@ -0,0 +1,38 @@
1
+ import { join } from 'node:path'
2
+ import type { DevupApiOptions } from '@devup-api/core'
3
+ import { createUrlMap, generateInterface } from '@devup-api/generator'
4
+ import { createTmpDir, readOpenapi, writeInterface } from '@devup-api/utils'
5
+ import { devupApiWebpackPlugin } from '@devup-api/webpack-plugin'
6
+ import type { NextConfig } from 'next'
7
+
8
+ export function devupApi(
9
+ config: NextConfig,
10
+ options: DevupApiOptions = {},
11
+ ): NextConfig {
12
+ const isTurbo =
13
+ process.env.TURBOPACK === '1' || process.env.TURBOPACK === 'auto'
14
+ if (isTurbo) {
15
+ const tempDir = createTmpDir(options?.tempDir)
16
+ const schema = readOpenapi(options?.openapiFile)
17
+
18
+ writeInterface(
19
+ join(tempDir, 'api.d.ts'),
20
+ generateInterface(schema, options),
21
+ )
22
+ // Create urlMap and set environment variable
23
+ const urlMap = createUrlMap(schema, options)
24
+ config.env ??= {}
25
+ Object.assign(config.env, {
26
+ DEVUP_API_URL_MAP: JSON.stringify(urlMap),
27
+ })
28
+ return config
29
+ }
30
+
31
+ const webpack = config.webpack
32
+ config.webpack = (config, _options) => {
33
+ config.plugins.push(new devupApiWebpackPlugin(options))
34
+ if (typeof webpack === 'function') return webpack(config, _options)
35
+ return config
36
+ }
37
+ return config
38
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+
11
+ // Bundler mode
12
+ "moduleResolution": "bundler",
13
+ "verbatimModuleSyntax": true,
14
+ "emitDeclarationOnly": true,
15
+
16
+ // Best practices
17
+ "strict": true,
18
+ "skipLibCheck": true,
19
+ "noFallthroughCasesInSwitch": true,
20
+ "noUncheckedIndexedAccess": true,
21
+ "noImplicitOverride": true,
22
+
23
+ // Some stricter flags (disabled by default)
24
+ "noUnusedLocals": false,
25
+ "noUnusedParameters": false,
26
+ "noPropertyAccessFromIndexSignature": false,
27
+ "declaration": true,
28
+ "declarationMap": true,
29
+ "outDir": "dist",
30
+ "rootDir": "src"
31
+ },
32
+ "include": ["src/**/*.ts"],
33
+ "exclude": ["dist", "node_modules"]
34
+ }