@devup-api/rsbuild-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,24 @@
1
+ # @devup-api/rsbuild-plugin
2
+
3
+ devup API plugin for Rsbuild
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @devup-api/rsbuild-plugin
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import devupApiRsbuildPlugin from '@devup-api/rsbuild-plugin';
15
+ import { defineConfig } from '@rsbuild/core';
16
+
17
+ export default defineConfig({
18
+ plugins: [
19
+ devupApiRsbuildPlugin({
20
+ // options
21
+ }),
22
+ ],
23
+ });
24
+ ```
@@ -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:E,getOwnPropertyNames:K,getOwnPropertyDescriptor:L}=Object,N=Object.prototype.hasOwnProperty;var G=new WeakMap,Q=(x)=>{var P=G.get(x),k;if(P)return P;if(P=E({},"__esModule",{value:!0}),x&&typeof x==="object"||typeof x==="function")K(x).map((R)=>!N.call(P,R)&&E(P,R,{get:()=>x[R],enumerable:!(k=L(x,R))||k.enumerable}));return G.set(x,P),P};var S=(x,P)=>{for(var k in P)E(x,k,{get:P[k],enumerable:!0,configurable:!0,set:(R)=>P[k]=()=>R})};var V={};S(V,{devupApiRsbuildPlugin:()=>J,default:()=>J});module.exports=Q(V);var H=require("node:path"),B=require("@devup-api/generator"),q=require("@devup-api/utils");function J(x){return{name:"devup-api",async setup(P){let k=await q.createTmpDirAsync(x?.tempDir),R=await q.readOpenapiAsync(x?.openapiFile);await q.writeInterfaceAsync(H.join(k,"api.d.ts"),B.generateInterface(R,x));let F=B.createUrlMap(R,x);P.modifyRsbuildConfig((z)=>{if(z.source??={},z.source.define??={},F)z.source.define["process.env.DEVUP_API_URL_MAP"]=JSON.stringify(JSON.stringify(F));return z})}}}
@@ -0,0 +1,3 @@
1
+ export * from './plugin';
2
+ export { devupApiRsbuildPlugin 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,qBAAqB,IAAI,OAAO,EAAE,MAAM,UAAU,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import{join as B}from"node:path";import{createUrlMap as E,generateInterface as F}from"@devup-api/generator";import{createTmpDirAsync as G,readOpenapiAsync as H,writeInterfaceAsync as J}from"@devup-api/utils";function K(x){return{name:"devup-api",async setup(q){let z=await G(x?.tempDir),R=await H(x?.openapiFile);await J(B(z,"api.d.ts"),F(R,x));let k=E(R,x);q.modifyRsbuildConfig((P)=>{if(P.source??={},P.source.define??={},k)P.source.define["process.env.DEVUP_API_URL_MAP"]=JSON.stringify(JSON.stringify(k));return P})}}}export{K as devupApiRsbuildPlugin,K as default};
@@ -0,0 +1,4 @@
1
+ import type { DevupApiOptions } from '@devup-api/core';
2
+ import type { RsbuildPlugin } from '@rsbuild/core';
3
+ export declare function devupApiRsbuildPlugin(options?: DevupApiOptions): RsbuildPlugin;
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;AAOtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAElD,wBAAgB,qBAAqB,CACnC,OAAO,CAAC,EAAE,eAAe,GACxB,aAAa,CA2Bf"}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@devup-api/rsbuild-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
+ },
23
+ "peerDependencies": {
24
+ "@rsbuild/core": "*",
25
+ "@devup-api/core": "*"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^24.10",
29
+ "typescript": "^5.9"
30
+ }
31
+ }
@@ -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
+ devupApiRsbuildPlugin: expect.any(Function),
7
+ default: expect.any(Function),
8
+ })
9
+ })
@@ -0,0 +1,228 @@
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 { devupApiRsbuildPlugin } from '../plugin'
7
+
8
+ let mockCreateTmpDirAsync: ReturnType<typeof spyOn>
9
+ let mockReadOpenapiAsync: ReturnType<typeof spyOn>
10
+ let mockWriteInterfaceAsync: ReturnType<typeof spyOn>
11
+ let mockCreateUrlMap: ReturnType<typeof spyOn>
12
+ let mockGenerateInterface: ReturnType<typeof spyOn>
13
+
14
+ const mockSchema = {
15
+ openapi: '3.1.0',
16
+ paths: {
17
+ '/users': {
18
+ get: {
19
+ operationId: 'getUsers',
20
+ responses: {
21
+ '200': {
22
+ content: {
23
+ 'application/json': {
24
+ schema: {
25
+ type: 'array',
26
+ items: { type: 'string' },
27
+ },
28
+ },
29
+ },
30
+ },
31
+ },
32
+ },
33
+ },
34
+ },
35
+ } as const
36
+
37
+ const mockUrlMap = {
38
+ getUsers: {
39
+ method: 'GET' as const,
40
+ url: '/users',
41
+ },
42
+ '/users': {
43
+ method: 'GET' as const,
44
+ url: '/users',
45
+ },
46
+ }
47
+
48
+ const mockInterfaceContent = 'export interface Test {}'
49
+
50
+ const createMockBuild = () => {
51
+ const modifyRsbuildConfigMock = mock(
52
+ (modifier: (config: unknown) => unknown) => {
53
+ const config = { source: { define: {} } }
54
+ return modifier(config)
55
+ },
56
+ )
57
+ return {
58
+ modifyRsbuildConfig: modifyRsbuildConfigMock,
59
+ }
60
+ }
61
+
62
+ beforeEach(() => {
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
+ mockCreateTmpDirAsync.mockClear()
80
+ mockReadOpenapiAsync.mockClear()
81
+ mockWriteInterfaceAsync.mockClear()
82
+ mockCreateUrlMap.mockClear()
83
+ mockGenerateInterface.mockClear()
84
+ })
85
+
86
+ test('devupApiRsbuildPlugin returns plugin with correct name', () => {
87
+ const plugin = devupApiRsbuildPlugin()
88
+ expect(plugin.name).toBe('devup-api')
89
+ })
90
+
91
+ test.each([
92
+ [undefined],
93
+ [{ tempDir: 'custom-dir' }],
94
+ [{ openapiFile: 'custom-openapi.json' }],
95
+ [
96
+ {
97
+ tempDir: 'custom-dir',
98
+ openapiFile: 'custom-openapi.json',
99
+ convertCase: 'snake' as const,
100
+ },
101
+ ],
102
+ ] as const)('devupApiRsbuildPlugin returns plugin with setup hook: %s', async (options:
103
+ | DevupApiOptions
104
+ | undefined) => {
105
+ const plugin = devupApiRsbuildPlugin(options)
106
+ expect(plugin.setup).toBeDefined()
107
+ expect(typeof plugin.setup).toBe('function')
108
+
109
+ const build = createMockBuild()
110
+ await plugin.setup?.(build as never)
111
+
112
+ expect(mockCreateTmpDirAsync).toHaveBeenCalledWith(options?.tempDir)
113
+ expect(mockReadOpenapiAsync).toHaveBeenCalledWith(options?.openapiFile)
114
+ expect(mockGenerateInterface).toHaveBeenCalledWith(mockSchema, options)
115
+ expect(mockWriteInterfaceAsync).toHaveBeenCalledWith(
116
+ join('df', 'api.d.ts'),
117
+ mockInterfaceContent,
118
+ )
119
+ expect(mockCreateUrlMap).toHaveBeenCalledWith(mockSchema, options)
120
+ expect(build.modifyRsbuildConfig).toHaveBeenCalled()
121
+ })
122
+
123
+ test('devupApiRsbuildPlugin setup hook modifies config with urlMap', async () => {
124
+ const plugin = devupApiRsbuildPlugin()
125
+ const build = createMockBuild()
126
+ await plugin.setup?.(build as never)
127
+
128
+ const configModifier = (build.modifyRsbuildConfig as ReturnType<typeof mock>)
129
+ .mock.calls[0]?.[0] as (config: {
130
+ source?: { define?: Record<string, string> }
131
+ }) => unknown
132
+ const config = { source: { define: {} } }
133
+ const result = configModifier(config)
134
+
135
+ expect(result).toEqual({
136
+ source: {
137
+ define: {
138
+ 'process.env.DEVUP_API_URL_MAP': JSON.stringify(
139
+ JSON.stringify(mockUrlMap),
140
+ ),
141
+ },
142
+ },
143
+ })
144
+ })
145
+
146
+ test('devupApiRsbuildPlugin setup hook handles config without source', async () => {
147
+ const plugin = devupApiRsbuildPlugin()
148
+ const build = createMockBuild()
149
+ await plugin.setup?.(build as never)
150
+
151
+ const configModifier = (build.modifyRsbuildConfig as ReturnType<typeof mock>)
152
+ .mock.calls[0]?.[0] as (config: Record<string, unknown>) => unknown
153
+ const config = {}
154
+ const result = configModifier(config)
155
+
156
+ expect(result).toEqual({
157
+ source: {
158
+ define: {
159
+ 'process.env.DEVUP_API_URL_MAP': JSON.stringify(
160
+ JSON.stringify(mockUrlMap),
161
+ ),
162
+ },
163
+ },
164
+ })
165
+ })
166
+
167
+ test('devupApiRsbuildPlugin setup hook handles config without define', async () => {
168
+ const plugin = devupApiRsbuildPlugin()
169
+ const build = createMockBuild()
170
+ await plugin.setup?.(build as never)
171
+
172
+ const configModifier = (build.modifyRsbuildConfig as ReturnType<typeof mock>)
173
+ .mock.calls[0]?.[0] as (config: {
174
+ source?: Record<string, unknown>
175
+ }) => unknown
176
+ const config = { source: {} }
177
+ const result = configModifier(config)
178
+
179
+ expect(result).toEqual({
180
+ source: {
181
+ define: {
182
+ 'process.env.DEVUP_API_URL_MAP': JSON.stringify(
183
+ JSON.stringify(mockUrlMap),
184
+ ),
185
+ },
186
+ },
187
+ })
188
+ })
189
+
190
+ test('devupApiRsbuildPlugin setup hook does not add urlMap when urlMap is null', async () => {
191
+ mockCreateUrlMap.mockReturnValueOnce(null as never)
192
+ const plugin = devupApiRsbuildPlugin()
193
+ const build = createMockBuild()
194
+ await plugin.setup?.(build as never)
195
+
196
+ const configModifier = (build.modifyRsbuildConfig as ReturnType<typeof mock>)
197
+ .mock.calls[0]?.[0] as (config: {
198
+ source?: { define?: Record<string, string> }
199
+ }) => unknown
200
+ const config = { source: { define: {} } }
201
+ const result = configModifier(config)
202
+
203
+ expect(result).toEqual({
204
+ source: {
205
+ define: {},
206
+ },
207
+ })
208
+ })
209
+
210
+ test('devupApiRsbuildPlugin setup hook does not add urlMap when urlMap is undefined', async () => {
211
+ mockCreateUrlMap.mockReturnValueOnce(undefined as never)
212
+ const plugin = devupApiRsbuildPlugin()
213
+ const build = createMockBuild()
214
+ await plugin.setup?.(build as never)
215
+
216
+ const configModifier = (build.modifyRsbuildConfig as ReturnType<typeof mock>)
217
+ .mock.calls[0]?.[0] as (config: {
218
+ source?: { define?: Record<string, string> }
219
+ }) => unknown
220
+ const config = { source: { define: {} } }
221
+ const result = configModifier(config)
222
+
223
+ expect(result).toEqual({
224
+ source: {
225
+ define: {},
226
+ },
227
+ })
228
+ })
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './plugin'
2
+ export { devupApiRsbuildPlugin as default } from './plugin'
package/src/plugin.ts ADDED
@@ -0,0 +1,40 @@
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 { RsbuildPlugin } from '@rsbuild/core'
10
+
11
+ export function devupApiRsbuildPlugin(
12
+ options?: DevupApiOptions,
13
+ ): RsbuildPlugin {
14
+ return {
15
+ name: 'devup-api',
16
+ async setup(build) {
17
+ const tempDir = await createTmpDirAsync(options?.tempDir)
18
+ const schema = await readOpenapiAsync(options?.openapiFile)
19
+
20
+ // Generate interface file
21
+ await writeInterfaceAsync(
22
+ join(tempDir, 'api.d.ts'),
23
+ generateInterface(schema, options),
24
+ )
25
+
26
+ // Create urlMap and set environment variable
27
+ const urlMap = createUrlMap(schema, options)
28
+
29
+ build.modifyRsbuildConfig((config) => {
30
+ config.source ??= {}
31
+ config.source.define ??= {}
32
+ if (urlMap) {
33
+ config.source.define['process.env.DEVUP_API_URL_MAP'] =
34
+ JSON.stringify(JSON.stringify(urlMap))
35
+ }
36
+ return config
37
+ })
38
+ },
39
+ }
40
+ }
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
+ }