@devup-api/webpack-plugin 0.1.0 → 0.1.2
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/package.json +8 -4
- package/dist/__tests__/index.test.d.ts +0 -2
- package/dist/__tests__/index.test.d.ts.map +0 -1
- package/dist/__tests__/plugin.test.d.ts +0 -2
- package/dist/__tests__/plugin.test.d.ts.map +0 -1
- package/src/__tests__/index.test.ts +0 -9
- package/src/__tests__/plugin.test.ts +0 -263
- package/src/index.ts +0 -2
- package/src/plugin.ts +0 -65
- package/tsconfig.json +0 -34
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devup-api/webpack-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"license": "Apache-2.0",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"exports": {
|
|
6
7
|
".": {
|
|
@@ -9,6 +10,9 @@
|
|
|
9
10
|
"types": "./dist/index.d.ts"
|
|
10
11
|
}
|
|
11
12
|
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
12
16
|
"scripts": {
|
|
13
17
|
"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
18
|
},
|
|
@@ -16,9 +20,9 @@
|
|
|
16
20
|
"access": "public"
|
|
17
21
|
},
|
|
18
22
|
"dependencies": {
|
|
19
|
-
"@devup-api/utils": "0.1.
|
|
20
|
-
"@devup-api/core": "0.1.
|
|
21
|
-
"@devup-api/generator": "0.1.
|
|
23
|
+
"@devup-api/utils": "0.1.2",
|
|
24
|
+
"@devup-api/core": "0.1.2",
|
|
25
|
+
"@devup-api/generator": "0.1.2"
|
|
22
26
|
},
|
|
23
27
|
"peerDependencies": {
|
|
24
28
|
"webpack": "*",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/index.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/plugin.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,263 +0,0 @@
|
|
|
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 type { Compiler } from 'webpack'
|
|
7
|
-
import { DefinePlugin } from 'webpack'
|
|
8
|
-
import { devupApiWebpackPlugin } from '../plugin'
|
|
9
|
-
|
|
10
|
-
let mockCreateTmpDirAsync: ReturnType<typeof spyOn>
|
|
11
|
-
let mockReadOpenapiAsync: ReturnType<typeof spyOn>
|
|
12
|
-
let mockWriteInterfaceAsync: ReturnType<typeof spyOn>
|
|
13
|
-
let mockCreateUrlMap: ReturnType<typeof spyOn>
|
|
14
|
-
let mockGenerateInterface: ReturnType<typeof spyOn>
|
|
15
|
-
|
|
16
|
-
const mockSchema = {
|
|
17
|
-
openapi: '3.1.0',
|
|
18
|
-
paths: {
|
|
19
|
-
'/users': {
|
|
20
|
-
get: {
|
|
21
|
-
operationId: 'getUsers',
|
|
22
|
-
responses: {
|
|
23
|
-
'200': {
|
|
24
|
-
content: {
|
|
25
|
-
'application/json': {
|
|
26
|
-
schema: {
|
|
27
|
-
type: 'array',
|
|
28
|
-
items: { type: 'string' },
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
} as const
|
|
38
|
-
|
|
39
|
-
const mockUrlMap = {
|
|
40
|
-
getUsers: {
|
|
41
|
-
method: 'GET' as const,
|
|
42
|
-
url: '/users',
|
|
43
|
-
},
|
|
44
|
-
'/users': {
|
|
45
|
-
method: 'GET' as const,
|
|
46
|
-
url: '/users',
|
|
47
|
-
},
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const mockInterfaceContent = 'export interface Test {}'
|
|
51
|
-
|
|
52
|
-
const createMockCompiler = (): Compiler & {
|
|
53
|
-
_storedCallback?: (params: unknown, cb: (error?: Error) => void) => void
|
|
54
|
-
} => {
|
|
55
|
-
const storedCallback: {
|
|
56
|
-
callback?: (params: unknown, cb: (error?: Error) => void) => void
|
|
57
|
-
} = {}
|
|
58
|
-
const tapAsyncMock = mock(
|
|
59
|
-
(
|
|
60
|
-
_name: string,
|
|
61
|
-
callback: (params: unknown, cb: (error?: Error) => void) => void,
|
|
62
|
-
) => {
|
|
63
|
-
storedCallback.callback = callback
|
|
64
|
-
},
|
|
65
|
-
)
|
|
66
|
-
const hooks = {
|
|
67
|
-
beforeCompile: {
|
|
68
|
-
tapAsync: tapAsyncMock,
|
|
69
|
-
},
|
|
70
|
-
}
|
|
71
|
-
const compiler = {
|
|
72
|
-
hooks,
|
|
73
|
-
} as unknown as Compiler & {
|
|
74
|
-
_storedCallback?: (params: unknown, cb: (error?: Error) => void) => void
|
|
75
|
-
}
|
|
76
|
-
Object.defineProperty(compiler, '_storedCallback', {
|
|
77
|
-
get() {
|
|
78
|
-
return storedCallback.callback
|
|
79
|
-
},
|
|
80
|
-
enumerable: true,
|
|
81
|
-
configurable: true,
|
|
82
|
-
})
|
|
83
|
-
return compiler
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
beforeEach(() => {
|
|
87
|
-
mockCreateTmpDirAsync = spyOn(utils, 'createTmpDirAsync').mockResolvedValue(
|
|
88
|
-
'df',
|
|
89
|
-
)
|
|
90
|
-
mockReadOpenapiAsync = spyOn(utils, 'readOpenapiAsync').mockResolvedValue(
|
|
91
|
-
mockSchema as never,
|
|
92
|
-
)
|
|
93
|
-
mockWriteInterfaceAsync = spyOn(
|
|
94
|
-
utils,
|
|
95
|
-
'writeInterfaceAsync',
|
|
96
|
-
).mockResolvedValue(undefined)
|
|
97
|
-
mockCreateUrlMap = spyOn(generator, 'createUrlMap').mockReturnValue(
|
|
98
|
-
mockUrlMap as never,
|
|
99
|
-
)
|
|
100
|
-
mockGenerateInterface = spyOn(generator, 'generateInterface').mockReturnValue(
|
|
101
|
-
mockInterfaceContent,
|
|
102
|
-
)
|
|
103
|
-
mockCreateTmpDirAsync.mockClear()
|
|
104
|
-
mockReadOpenapiAsync.mockClear()
|
|
105
|
-
mockWriteInterfaceAsync.mockClear()
|
|
106
|
-
mockCreateUrlMap.mockClear()
|
|
107
|
-
mockGenerateInterface.mockClear()
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
test('devupApiWebpackPlugin constructor initializes with default options', () => {
|
|
111
|
-
const plugin = new devupApiWebpackPlugin()
|
|
112
|
-
expect(plugin.options).toEqual({})
|
|
113
|
-
expect(plugin.initialized).toBe(false)
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
test.each([
|
|
117
|
-
[{ tempDir: 'custom-dir' }],
|
|
118
|
-
[{ openapiFile: 'custom-openapi.json' }],
|
|
119
|
-
[
|
|
120
|
-
{
|
|
121
|
-
tempDir: 'custom-dir',
|
|
122
|
-
openapiFile: 'custom-openapi.json',
|
|
123
|
-
convertCase: 'snake' as const,
|
|
124
|
-
},
|
|
125
|
-
],
|
|
126
|
-
] as const)('devupApiWebpackPlugin constructor initializes with options: %s', (options: DevupApiOptions) => {
|
|
127
|
-
const plugin = new devupApiWebpackPlugin(options)
|
|
128
|
-
expect(plugin.options).toEqual(options)
|
|
129
|
-
expect(plugin.initialized).toBe(false)
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
test('devupApiWebpackPlugin apply method registers beforeCompile hook', () => {
|
|
133
|
-
const plugin = new devupApiWebpackPlugin()
|
|
134
|
-
const compiler = createMockCompiler()
|
|
135
|
-
plugin.apply(compiler)
|
|
136
|
-
expect(compiler.hooks.beforeCompile.tapAsync).toHaveBeenCalledWith(
|
|
137
|
-
'devup-api',
|
|
138
|
-
expect.any(Function),
|
|
139
|
-
)
|
|
140
|
-
expect(compiler._storedCallback).toBeDefined()
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
test.each([
|
|
144
|
-
[undefined],
|
|
145
|
-
[{ tempDir: 'custom-dir' }],
|
|
146
|
-
[{ openapiFile: 'custom-openapi.json' }],
|
|
147
|
-
[
|
|
148
|
-
{
|
|
149
|
-
tempDir: 'custom-dir',
|
|
150
|
-
openapiFile: 'custom-openapi.json',
|
|
151
|
-
convertCase: 'pascal' as const,
|
|
152
|
-
},
|
|
153
|
-
],
|
|
154
|
-
] as const)('devupApiWebpackPlugin beforeCompile hook executes correctly: %s', async (options:
|
|
155
|
-
| DevupApiOptions
|
|
156
|
-
| undefined) => {
|
|
157
|
-
const plugin = new devupApiWebpackPlugin(options)
|
|
158
|
-
const compiler = createMockCompiler()
|
|
159
|
-
const definePluginApplySpy = spyOn(
|
|
160
|
-
DefinePlugin.prototype,
|
|
161
|
-
'apply',
|
|
162
|
-
).mockImplementation(() => {})
|
|
163
|
-
plugin.apply(compiler)
|
|
164
|
-
|
|
165
|
-
const callback = compiler._storedCallback
|
|
166
|
-
expect(callback).toBeDefined()
|
|
167
|
-
|
|
168
|
-
const mockCallback = mock(() => {})
|
|
169
|
-
await callback?.(null, mockCallback)
|
|
170
|
-
|
|
171
|
-
expect(mockCreateTmpDirAsync).toHaveBeenCalledWith(options?.tempDir)
|
|
172
|
-
expect(mockReadOpenapiAsync).toHaveBeenCalledWith(options?.openapiFile)
|
|
173
|
-
expect(mockGenerateInterface).toHaveBeenCalledWith(mockSchema, options || {})
|
|
174
|
-
expect(mockWriteInterfaceAsync).toHaveBeenCalledWith(
|
|
175
|
-
join('df', 'api.d.ts'),
|
|
176
|
-
mockInterfaceContent,
|
|
177
|
-
)
|
|
178
|
-
expect(mockCreateUrlMap).toHaveBeenCalledWith(mockSchema, options || {})
|
|
179
|
-
expect(definePluginApplySpy).toHaveBeenCalledWith(compiler)
|
|
180
|
-
expect(mockCallback).toHaveBeenCalled()
|
|
181
|
-
expect(plugin.initialized).toBe(true)
|
|
182
|
-
definePluginApplySpy.mockRestore()
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
test('devupApiWebpackPlugin beforeCompile hook does not add DefinePlugin when urlMap is null', async () => {
|
|
186
|
-
mockCreateUrlMap.mockReturnValueOnce(null as never)
|
|
187
|
-
const plugin = new devupApiWebpackPlugin()
|
|
188
|
-
const compiler = createMockCompiler()
|
|
189
|
-
const definePluginApplySpy = spyOn(
|
|
190
|
-
DefinePlugin.prototype,
|
|
191
|
-
'apply',
|
|
192
|
-
).mockImplementation(() => {})
|
|
193
|
-
plugin.apply(compiler)
|
|
194
|
-
|
|
195
|
-
const callback = compiler._storedCallback
|
|
196
|
-
|
|
197
|
-
const mockCallback = mock(() => {})
|
|
198
|
-
await callback?.(null, mockCallback)
|
|
199
|
-
|
|
200
|
-
expect(definePluginApplySpy).not.toHaveBeenCalled()
|
|
201
|
-
expect(mockCallback).toHaveBeenCalled()
|
|
202
|
-
definePluginApplySpy.mockRestore()
|
|
203
|
-
})
|
|
204
|
-
|
|
205
|
-
test('devupApiWebpackPlugin beforeCompile hook does not add DefinePlugin when urlMap is undefined', async () => {
|
|
206
|
-
mockCreateUrlMap.mockReturnValueOnce(undefined as never)
|
|
207
|
-
const plugin = new devupApiWebpackPlugin()
|
|
208
|
-
const compiler = createMockCompiler()
|
|
209
|
-
const definePluginApplySpy = spyOn(
|
|
210
|
-
DefinePlugin.prototype,
|
|
211
|
-
'apply',
|
|
212
|
-
).mockImplementation(() => {})
|
|
213
|
-
plugin.apply(compiler)
|
|
214
|
-
|
|
215
|
-
const callback = compiler._storedCallback
|
|
216
|
-
|
|
217
|
-
const mockCallback = mock(() => {})
|
|
218
|
-
await callback?.(null, mockCallback)
|
|
219
|
-
|
|
220
|
-
expect(definePluginApplySpy).not.toHaveBeenCalled()
|
|
221
|
-
expect(mockCallback).toHaveBeenCalled()
|
|
222
|
-
definePluginApplySpy.mockRestore()
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
test('devupApiWebpackPlugin beforeCompile hook only runs once when called multiple times', async () => {
|
|
226
|
-
const plugin = new devupApiWebpackPlugin()
|
|
227
|
-
const compiler = createMockCompiler()
|
|
228
|
-
plugin.apply(compiler)
|
|
229
|
-
|
|
230
|
-
const callback = compiler._storedCallback
|
|
231
|
-
|
|
232
|
-
const mockCallback1 = mock(() => {})
|
|
233
|
-
const mockCallback2 = mock(() => {})
|
|
234
|
-
|
|
235
|
-
await Promise.all([
|
|
236
|
-
callback?.(null, mockCallback1),
|
|
237
|
-
callback?.(null, mockCallback2),
|
|
238
|
-
])
|
|
239
|
-
|
|
240
|
-
expect(mockCreateTmpDirAsync).toHaveBeenCalledTimes(1)
|
|
241
|
-
expect(mockReadOpenapiAsync).toHaveBeenCalledTimes(1)
|
|
242
|
-
expect(mockGenerateInterface).toHaveBeenCalledTimes(1)
|
|
243
|
-
expect(mockWriteInterfaceAsync).toHaveBeenCalledTimes(1)
|
|
244
|
-
expect(mockCreateUrlMap).toHaveBeenCalledTimes(1)
|
|
245
|
-
expect(mockCallback1).toHaveBeenCalled()
|
|
246
|
-
expect(mockCallback2).toHaveBeenCalled()
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
test('devupApiWebpackPlugin beforeCompile hook handles errors correctly', async () => {
|
|
250
|
-
const error = new Error('Test error')
|
|
251
|
-
mockCreateTmpDirAsync.mockRejectedValueOnce(error)
|
|
252
|
-
const plugin = new devupApiWebpackPlugin()
|
|
253
|
-
const compiler = createMockCompiler()
|
|
254
|
-
plugin.apply(compiler)
|
|
255
|
-
|
|
256
|
-
const callback = compiler._storedCallback
|
|
257
|
-
|
|
258
|
-
const mockCallback = mock(() => {})
|
|
259
|
-
await callback?.(null, mockCallback)
|
|
260
|
-
|
|
261
|
-
expect(mockCallback).toHaveBeenCalledWith(error)
|
|
262
|
-
expect(plugin.initialized).toBe(false)
|
|
263
|
-
})
|
package/src/index.ts
DELETED
package/src/plugin.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { join } from 'node:path'
|
|
2
|
-
import type { DevupApiOptions } from '@devup-api/core'
|
|
3
|
-
import { createUrlMap, generateInterface } from '@devup-api/generator'
|
|
4
|
-
import {
|
|
5
|
-
createTmpDirAsync,
|
|
6
|
-
readOpenapiAsync,
|
|
7
|
-
writeInterfaceAsync,
|
|
8
|
-
} from '@devup-api/utils'
|
|
9
|
-
import type { Compiler } from 'webpack'
|
|
10
|
-
import { DefinePlugin } from 'webpack'
|
|
11
|
-
|
|
12
|
-
export class devupApiWebpackPlugin {
|
|
13
|
-
options: DevupApiOptions
|
|
14
|
-
initialized = false
|
|
15
|
-
|
|
16
|
-
constructor(options?: DevupApiOptions) {
|
|
17
|
-
this.options = options || {}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
apply(compiler: Compiler): void {
|
|
21
|
-
// Perform async operations before compilation
|
|
22
|
-
compiler.hooks.beforeCompile.tapAsync(
|
|
23
|
-
'devup-api',
|
|
24
|
-
async (_params, callback) => {
|
|
25
|
-
// Guard: only run once
|
|
26
|
-
if (this.initialized) {
|
|
27
|
-
callback()
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
try {
|
|
32
|
-
this.initialized = true
|
|
33
|
-
|
|
34
|
-
const tempDir = await createTmpDirAsync(this.options?.tempDir)
|
|
35
|
-
const schema = await readOpenapiAsync(this.options?.openapiFile)
|
|
36
|
-
|
|
37
|
-
// Generate interface file
|
|
38
|
-
await writeInterfaceAsync(
|
|
39
|
-
join(tempDir, 'api.d.ts'),
|
|
40
|
-
generateInterface(schema, this.options),
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
// Create urlMap and set environment variable
|
|
44
|
-
const urlMap = createUrlMap(schema, this.options)
|
|
45
|
-
const define: Record<string, string> = {}
|
|
46
|
-
if (urlMap) {
|
|
47
|
-
define['process.env.DEVUP_API_URL_MAP'] = JSON.stringify(
|
|
48
|
-
JSON.stringify(urlMap),
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Add DefinePlugin to webpack configuration
|
|
53
|
-
if (Object.keys(define).length > 0) {
|
|
54
|
-
new DefinePlugin(define).apply(compiler)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
callback()
|
|
58
|
-
} catch (error) {
|
|
59
|
-
this.initialized = false
|
|
60
|
-
callback(error as Error)
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
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
|
-
}
|