@yunarch/config-web 0.5.6 → 0.5.8
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 +1 -1
- package/dist/cli/openapi-sync/openapi-sync-lint-msw-handlers.cli.js +2 -2
- package/dist/cli/openapi-sync/openapi-sync.cli.js +14 -8
- package/dist/config.biome.json +1 -0
- package/dist/{eslint.config.d.ts → config.eslint.d.ts} +970 -288
- package/dist/config.eslint.js +1 -0
- package/dist/config.oxlint.json +1 -0
- package/dist/config.prettier.d.ts +5 -0
- package/dist/config.prettier.js +1 -0
- package/package.json +48 -49
- package/dist/biome.config.json +0 -1
- package/dist/eslint.config.js +0 -1
- package/dist/oxlint.config.json +0 -1
- package/dist/prettier.config.js +0 -1
- /package/dist/{ts/tsconfig-base.json → config.tsconfig-base.json} +0 -0
package/README.md
CHANGED
|
@@ -248,7 +248,7 @@ To use the oxlint linter, create a `.oxlintrc.json` [configuration file](https:/
|
|
|
248
248
|
|
|
249
249
|
> [!CAUTION]
|
|
250
250
|
> Currently, `Oxlint` does not resolve configuration file paths automatically. To extend a config, you must explicitly provide the full path, like so:
|
|
251
|
-
> `"extends": ["./node_modules/@yunarch/config-web/dist/oxlint.
|
|
251
|
+
> `"extends": ["./node_modules/@yunarch/config-web/dist/config.oxlint.json"]`
|
|
252
252
|
|
|
253
253
|
### Enabling ESLint and Oxlint Simultaneously
|
|
254
254
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{c as M}from"../chunk-LXZC4N54.js";import{existsSync as N,statSync as
|
|
2
|
+
import{c as M}from"../chunk-LXZC4N54.js";import{existsSync as N,statSync as F}from"fs";import v from"path";import{styleText as e}from"util";import{styleText as m}from"util";function H(f){let c=new Map;for(let o of f)c.has(o.service.name)||c.set(o.service.name,{service:o.service,handlers:[]}),c.get(o.service.name)?.handlers.push(o);if(c.size===0){console.log(m("green","\u2714 No missing handlers found"));return}for(let{service:o,handlers:i}of c.values()){console.log(`${m("underline",o.name)}${m("gray",` (${o.path})`)}`);for(let[a,s]of i.entries()){let t=a===i.length-1;console.log(` ${t?"\u2514\u2500":"\u251C\u2500"} ${m("cyan",s.service.toHandleHttpMethod)} ${m("yellow",s.service.toHandleUrl)}`),console.log(` ${t?" ":"\u2502"} \u251C\u2500 ${m("gray","Used in:")}`);for(let[p,l]of s.usedIn.entries()){let r=p===s.usedIn.length-1;console.log(` ${t?" ":"\u2502"} ${r?"\u2502 \u2514\u2500":"\u2502 \u251C\u2500"} ${l}`)}console.log(` ${t?" ":"\u2502"} \u2514\u2500 ${m("green","Suggested handler:")}`),console.log(` ${t?" ":"\u2502"} ${m("dim","\u2192")} ${s.suggestedPath}`),a<i.length-1&&console.log(` ${t?" ":"\u2502"}`)}console.log("")}console.log(m("red",`\u2718 ${f.length} missing ${f.length===1?"handler":"handlers"}`))}import{pathToFileURL as C}from"url";async function E({mswSetupFilePath:f,mswSetupConst:c}){let o=new Map,i=await import(C(f).href);if(!Object.hasOwn(i,c))throw new Error("MSW setup constant not found in the setup file");let a=i[c];if(!a||typeof a.listHandlers!="function")throw new Error("MSW setup constant does not have a listHandlers() method");let s=a.listHandlers();for(let t of s){if(!("info"in t)||!t.info?.path||!t.info.method)continue;let p=t.info.path,l=t.info.method.toUpperCase(),r=p.replaceAll(/:(?<temp1>[^/]+)/g,"{$1}"),d=`${l}:${r}`;o.set(d,{path:p,httpMethod:l,url:r})}return o}import{existsSync as W,readFileSync as U}from"fs";import I from"path";import _ from"fast-glob";import n from"typescript";async function T(f){let c=[],o=I.join(f,"services");if(!W(o))throw new Error(`Services directory not found: ${o}`);let i=await _("**/*Service.ts",{cwd:o,absolute:!0,ignore:["**/node_modules/**"]});for(let a of i){let s=I.basename(a,".ts"),t=U(a,"utf8"),p=n.createSourceFile(a,t,n.ScriptTarget.Latest,!0);n.forEachChild(p,l=>{if(n.isClassDeclaration(l)&&l.name?.text===s){for(let r of l.members)if(n.isMethodDeclaration(r)&&n.isIdentifier(r.name)&&r.modifiers&&r.modifiers.some(d=>d.kind===n.SyntaxKind.PublicKeyword)&&r.modifiers.some(d=>d.kind===n.SyntaxKind.StaticKeyword)){let d=r.name.text,y,h,x=u=>{if(n.isReturnStatement(u)&&u.expression&&n.isCallExpression(u.expression)&&n.isIdentifier(u.expression.expression)&&u.expression.expression.text==="__request"){let S=u.expression.arguments[1];if(n.isObjectLiteralExpression(S))for(let g of S.properties)n.isPropertyAssignment(g)&&n.isIdentifier(g.name)&&g.name.text==="url"&&n.isStringLiteral(g.initializer)?y=g.initializer.text:n.isPropertyAssignment(g)&&n.isIdentifier(g.name)&&g.name.text==="method"&&n.isStringLiteral(g.initializer)&&(h=g.initializer.text.toUpperCase())}n.forEachChild(u,x)};r.body&&n.forEachChild(r.body,x),y&&h?c.push({path:a,name:s,methodName:d,toHandleUrl:y,toHandleHttpMethod:h}):(y||console.warn(`No URL found for ${d} request in service ${s} (${a})`),h||console.warn(`No HTTP method found for ${d} request in service ${s} (${a})`))}}})}return c}async function P({genPath:f,srcPath:c}){let o=await T(f),i=new Map;for(let s of o)i.has(s.name)||i.set(s.name,new Map),i.get(s.name)?.set(s.methodName,{serviceInfo:s,files:new Set});let a=await _("**/*.{ts,tsx}",{cwd:c,absolute:!0,ignore:["**/node_modules/**","**/__tests__/**"]});for(let s of a)try{let t=U(s,"utf8"),p=n.createSourceFile(s,t,n.ScriptTarget.Latest,!0),l=r=>{if(n.isCallExpression(r)&&n.isPropertyAccessExpression(r.expression)&&n.isIdentifier(r.expression.expression)&&r.expression.expression.text.endsWith("Service")){let d=r.expression.expression.text,y=r.expression.name.text,h=i.get(d)?.get(y);if(!h)return;h.files.add(s)}n.forEachChild(r,l)};l(p)}catch(t){throw t instanceof Error?new Error(`Error parsing ${s}: ${t.message}`):new Error(`Error parsing ${s}: Unknown error`)}for(let[s,t]of i.entries()){for(let[p,l]of t.entries())l.files.size===0&&t.delete(p);t.size===0&&i.delete(s)}return i}import L from"path";function b(f,c,o){let i=[];for(let[a,s]of f.entries())for(let[t,p]of s.entries()){let{serviceInfo:l}=p,r=l.toHandleHttpMethod,d=l.toHandleUrl;!c.has(`${r}:${d}`)&&!c.has(`${r}:*${d}`)&&i.push({type:"missing_handler",service:l,usedIn:[...p.files],suggestedPath:L.join(o,`handlers/services/${a}/${t}.ts`)})}return i}M().name("openapi-sync-lint-msw-handlers").description("Lint MSW handlers against OpenAPI generated services from `openapi-sync`.\nIt checks for missing handlers based on generated services and your MSW setup.").requiredOption("--gen <path>","The output folder from `openapi-sync` script. Where the generated models and openapi schema and type definitions are saved.").requiredOption("--msw-setup-file <path>","Path to the MSW setup file (file that configures MSW setupServer or setupWorker).").requiredOption("--msw-setup-const <const>","Name of the constant that holds the MSW setup (e.g., server or worker).").addHelpText("after",`
|
|
3
3
|
Example usage:
|
|
4
4
|
${e("dim","$")} ${e("cyan","openapi-sync-lint-msw-handlers")} ${e("green","--gen")} ${e("yellow","./src/api/gen")} ${e("green","--msw-setup-file")} ${e("yellow","./src/api/__tests__/node.js")} ${e("green","--msw-setup-const")} ${e("yellow","server")}
|
|
5
5
|
|
|
@@ -10,4 +10,4 @@ Examples:
|
|
|
10
10
|
${e("dim","$")} ${e("yellow","tsx")} ${e("cyan","./node_modules/@yunarch/config-web/dist/cli/openapi-sync/openapi-sync-lint-msw-handlers.cli.js")} ${e("green","--gen")} ${e("yellow","./src/api/gen")} ${e("green","--msw-setup-file")} ${e("yellow","./src/api/__tests__/node.ts")} ${e("green","--msw-setup-const")} ${e("yellow","server")}
|
|
11
11
|
|
|
12
12
|
${e("dim","$")} ${e("yellow","bun")} ${e("yellow","--bun")} ${e("cyan","openapi-sync-lint-msw-handlers")} ${e("green","--gen")} ${e("yellow","./src/api/gen")} ${e("green","--msw-setup-file")} ${e("yellow","./src/api/__tests__/node.ts")} ${e("green","--msw-setup-const")} ${e("yellow","server")}
|
|
13
|
-
`).action(async({gen:f,mswSetupFile:c,mswSetupConst:o})=>{try{let i=process.cwd(),a=v.resolve(i,f),s=v.resolve(i,"."),t=v.resolve(i,c);if(!N(a)||!
|
|
13
|
+
`).action(async({gen:f,mswSetupFile:c,mswSetupConst:o})=>{try{let i=process.cwd(),a=v.resolve(i,f),s=v.resolve(i,"."),t=v.resolve(i,c);if(!N(a)||!F(a).isDirectory())throw new Error("Generated API folder does not exist or is not a directory");if(!N(t)||!F(t).isFile())throw new Error("MSW setup file does not exist or is not a file");let p=await P({genPath:a,srcPath:s}),l=await E({mswSetupFilePath:t,mswSetupConst:o}),r=b(p,l,v.dirname(t));H(r),process.exit(r.length>0?1:0)}catch(i){console.error(i),process.exit(1)}}).parseAsync(process.argv);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{a,b as
|
|
2
|
+
import{a as o,b as a,c as w}from"../chunk-LXZC4N54.js";import{existsSync as y}from"fs";import{mkdir as b,readFile as k,writeFile as O}from"fs/promises";import d from"path";import{styleText as n}from"util";import $ from"@inquirer/confirm";async function P(e,t){await a({name:"Generating models",command:async()=>{await o("npx",["openapi-typescript-codegen","--input",e,"--output",t,"--client","fetch"],{shell:!0})}})}import{writeFile as B}from"fs/promises";var x=`
|
|
3
3
|
import {
|
|
4
4
|
http as mswHttp,
|
|
5
5
|
type DefaultBodyType,
|
|
@@ -25,7 +25,13 @@ type HttpMethod =
|
|
|
25
25
|
/**
|
|
26
26
|
* Type guard to get the http methods available for a given path.
|
|
27
27
|
*/
|
|
28
|
-
type Methods<Path extends keyof Paths> =
|
|
28
|
+
type Methods<Path extends keyof Paths> = {
|
|
29
|
+
[M in keyof Paths[Path]]: M extends HttpMethod
|
|
30
|
+
? Paths[Path][M] extends undefined
|
|
31
|
+
? never
|
|
32
|
+
: M
|
|
33
|
+
: never;
|
|
34
|
+
}[keyof Paths[Path]];
|
|
29
35
|
|
|
30
36
|
/**
|
|
31
37
|
* Type guard to get the content type 'application/json' or 'multipart/form-data' of a type.
|
|
@@ -116,22 +122,22 @@ export function http<P extends keyof Paths, M extends Methods<P>>(
|
|
|
116
122
|
}
|
|
117
123
|
return handlers[method as keyof typeof handlers](uri, resolver, options);
|
|
118
124
|
}
|
|
119
|
-
`;async function T(e){await
|
|
120
|
-
${s}`)}})}async function C(e){if(d.extname(e)!=="")throw new Error("Output must be a directory.");let t=process.cwd(),s=d.resolve(e),r=s.startsWith(t)?s:d.resolve(t,d.relative(d.parse(e).root,e));return y(r)||await
|
|
125
|
+
`;async function T(e){await a({name:"Generating openapi MSW utils",command:async()=>{await B(`${e}/openapi-msw-http.ts`,x)}})}import{readFile as H,writeFile as E}from"fs/promises";async function M(e,t){await a({name:"Generating schema types",command:async()=>{await o("npx",["openapi-typescript",e,"-o",t],{shell:!0});let s=await H(t,"utf8");await E(t,`/* eslint-disable -- Autogenerated file */
|
|
126
|
+
${s}`)}})}async function C(e){if(d.extname(e)!=="")throw new Error("Output must be a directory.");let t=process.cwd(),s=d.resolve(e),r=s.startsWith(t)?s:d.resolve(t,d.relative(d.parse(e).root,e));return y(r)||await a({name:"Generating output directory",command:async()=>{await b(r,{recursive:!0})}}),r}async function S(e,t){let[s,i]=await Promise.all([a({name:"Reading input openapi schema",command:async()=>{if(!e.endsWith(".json"))throw new Error(`Input file must be a JSON file: ${e}`);if(e.startsWith("http"))try{let{stdout:r}=await o("curl",["-s",e,"--fail"],{encoding:"utf8"});return r}catch{throw new Error(`Failed to fetch remote OpenAPI file: ${e}`)}if(!y(e))throw new Error(`Input file does not exist: ${e}`);return await k(e,"utf8")}}),a({name:"Reading output openapi schema",command:async()=>{if(!t.endsWith(".json"))throw new Error(`Output file must be a JSON file: ${t}`);return y(t)?await k(t,"utf8"):!1}})]);return[JSON.stringify(JSON.parse(s)),i?JSON.stringify(JSON.parse(i)):!1]}w().name("openapi-sync").description("A CLI tool to convert OpenAPI 3.0/3.1 schemas to TypeScript types and create type-safe fetching based on a openapi file and keep them in sync.").requiredOption("-i, --input <path>","The input (local or remote) openapi schema (JSON).").requiredOption("-o, --output <folder>","The output folder to save the generated models and openapi schema and type definitions.").option("-y, --yes","Skip confirmation prompts and proceed with defaults.").option("-f, --force-gen","Force generation of typescript schemas and fetching code even if the input and output schemas are identical.").option("--include-msw-utils","Include MSW mocking utilities based on the generated typescript types.").option("--post-script <script>","A package.json script to run after the code generation.").option("--verify-openapi-sync","Verifies that the generated output is up to date with the input (e.g., in CI) to catch outdated or mismatched output without making changes.").addHelpText("after",`
|
|
121
127
|
Example usage:
|
|
122
128
|
${n("dim","$")} ${n("cyan","openapi-sync")} ${n("green","-i")} ${n("yellow","./openapi.json")} ${n("green","-o")} ${n("yellow","./src/api/gen")} ${n("green","--include-msw-utils")}
|
|
123
129
|
`).action(async({input:e,output:t,yes:s,forceGen:i,verifyOpenapiSync:r,includeMswUtils:R,postScript:m})=>{try{console.log(n("magenta",`
|
|
124
130
|
\u{1F680} openapi-sync
|
|
125
|
-
`));let p=await C(t),c=`${p}/openapi.json`,
|
|
131
|
+
`));let p=await C(t),c=`${p}/openapi.json`,v=`${p}/schema.d.ts`,[u,g]=await S(e,c),h=u!==g;r&&(console.log(h?n("yellow",`
|
|
126
132
|
\u26A0\uFE0F Local and remote schemas does not match!
|
|
127
133
|
`):n("green",`
|
|
128
134
|
\u2705 Local and remote schemas match!
|
|
129
|
-
`)),process.exit(h?1:0)),g===!1&&await
|
|
135
|
+
`)),process.exit(h?1:0)),g===!1&&await a({name:"Creating local schema",command:O(c,u)}),!h&&!i?(console.log(n("blue",`
|
|
130
136
|
No updates required.
|
|
131
137
|
`)),process.exit(0)):h&&(console.log(n("yellow",`
|
|
132
138
|
\u26A0\uFE0F Local and remote schemas does not match!
|
|
133
|
-
`)),s||await $({message:"Do you want to use the remote schema? (y/n)?"})?await
|
|
139
|
+
`)),s||await $({message:"Do you want to use the remote schema? (y/n)?"})?await a({name:"Replacing local schema with input schema",command:O(c,u)}):(console.log(n("yellow",`
|
|
134
140
|
\u26A0\uFE0F Sync remote schemas skipped.
|
|
135
|
-
`)),i||process.exit(0))),await Promise.all([M(c,
|
|
141
|
+
`)),i||process.exit(0))),await Promise.all([M(c,v),P(c,p)]),R&&await T(p),m&&await a({name:"Running post script",command:async()=>{try{await(typeof Bun<"u"?o("bun",["run",m]):o("node",["--run",m]))}catch{await o("npm",["run",m],{shell:!0})}}}),console.log(n("green",`
|
|
136
142
|
\u2705 openapi-sync process completed!
|
|
137
143
|
`))}catch(p){console.error(p),process.exit(1)}}).parseAsync(process.argv);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"formatter":{"enabled":true,"includes":["**","!**/node_modules/","!**/dist/","!**/out/","!**/output","!**/.output","!**/build/","!**/*.min.*","!**/package-lock.json","!**/yarn.lock","!**/.yarn/","!**/.yarnrc.yml","!**/.pnp.*","!**/.pnp","!**/.pnp.js","!**/.pnp.cjs","!**/bun.lock","!**/bun.lockb","!**/pnpm-lock.yaml","!**/.vite-inspect","!**/.vitepress/cache","!**/vite.config.*.timestamp-*","!**/*.log","!**/npm-debug.log*","!**/yarn-debug.log*","!**/yarn-error.log*","!**/coverage/","!**/.nyc_output/","!**/__snapshots__","!**/.vscode/","!**/.idea/","!**/.cache","!**/.nuxt","!**/.next","!**/.svelte-kit","!**/.vercel","!**/.changeset","!**/.turbo/","!**/.DS_Store","!**/Thumbs.db","!**/temp","!**/.temp","!**/tmp","!**/.tmp","!**/.history","!**/mockServiceWorker.js","!**/CHANGELOG*","!**/LICENSE*"],"useEditorconfig":false,"formatWithErrors":false,"lineWidth":80,"indentWidth":2,"indentStyle":"space","bracketSpacing":true,"bracketSameLine":false,"lineEnding":"lf","attributePosition":"auto"},"javascript":{"formatter":{"semicolons":"always","quoteStyle":"single","arrowParentheses":"always","trailingCommas":"es5","quoteProperties":"asNeeded","jsxQuoteStyle":"double"}},"linter":{"enabled":false,"includes":["!**/node_modules/","!**/dist/","!**/out/","!**/output","!**/.output","!**/build/","!**/*.min.*","!**/package-lock.json","!**/yarn.lock","!**/.yarn/","!**/.yarnrc.yml","!**/.pnp.*","!**/.pnp","!**/.pnp.js","!**/.pnp.cjs","!**/bun.lock","!**/bun.lockb","!**/pnpm-lock.yaml","!**/.vite-inspect","!**/.vitepress/cache","!**/vite.config.*.timestamp-*","!**/*.log","!**/npm-debug.log*","!**/yarn-debug.log*","!**/yarn-error.log*","!**/coverage/","!**/.nyc_output/","!**/__snapshots__","!**/.vscode/","!**/.idea/","!**/.cache","!**/.nuxt","!**/.next","!**/.svelte-kit","!**/.vercel","!**/.changeset","!**/.turbo/","!**/.DS_Store","!**/Thumbs.db","!**/temp","!**/.temp","!**/tmp","!**/.tmp","!**/.history","!**/mockServiceWorker.js","!**/CHANGELOG*","!**/LICENSE*"]},"overrides":[{"includes":["**/package.json"],"formatter":{"indentStyle":"space"}}],"assist":{"actions":{"source":{"organizeImports":"off"}}}}
|