From b2355b8b6fe3ef3f0b5f2308b8ca8596fda09168 Mon Sep 17 00:00:00 2001 From: Wongkraiwich Chuenchomphu Date: Sat, 22 Aug 2026 08:56:59 +0700 Subject: [PATCH] feat: ship lightweight Typeforge 0.2.0 --- .gitignore | 2 -- README.md | 6 +--- docs/cli.md | 2 -- package.json | 7 ++--- scripts/check-package.mjs | 47 +++++++++++++++++++++++++++++++ src/config/__tests__/load.test.ts | 35 +---------------------- src/config/load.ts | 9 ++---- src/config/types.ts | 3 -- src/index.ts | 1 - src/init/index.ts | 10 +++---- src/parser/loader.ts | 8 +----- test/e2e/cli.test.ts | 23 --------------- tsdown.config.ts | 7 ++--- 13 files changed, 62 insertions(+), 98 deletions(-) create mode 100644 scripts/check-package.mjs diff --git a/.gitignore b/.gitignore index d8e3c8d..4eb4fb3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,10 +2,8 @@ dist/ node_modules/ coverage/ .typeforge/ -.openapi-codegen/ test/fixtures/layouts/ *.tsbuildinfo .tmp/ typeforge.local.json -openapi-codegen.local.json .env diff --git a/README.md b/README.md index d8d5942..279ba7a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- Typeforge logo + Typeforge logo

# @openmirai/typeforge @@ -11,10 +11,6 @@ Headless **OpenAPI / Swagger → TypeScript** codegen. The CLI is `typeforge`. I You own `http.ts` (the `HTTPFetch` adapter). Generated files import that adapter — they do not invent axios/fetch calls inline. -## Migrating from `@openmirai/openapi-codegen` - -Install `@openmirai/typeforge` and update package imports and scripts to use the canonical `typeforge` name. During migration, the package also exposes the legacy `openapi-codegen` binary and reads `openapi-codegen.json`, `openapi-codegen.local.json`, and the `openapiCodegen` package.json key. New projects created by `typeforge init` use the Typeforge names. - ## What it generates For each **source** (a named API, e.g. `atlas`), under `//generated/`: diff --git a/docs/cli.md b/docs/cli.md index 5513e32..62c7c3c 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -2,8 +2,6 @@ Install `@openmirai/typeforge` from [npmjs](https://www.npmjs.com/package/@openmirai/typeforge). Add a script so the binary resolves from `node_modules/.bin`: -The legacy `openapi-codegen` binary and configuration filenames remain readable during migration, but all new usage should use `typeforge`. - ```json { "scripts": { diff --git a/package.json b/package.json index 8a6b42f..72538bb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@openmirai/typeforge", - "version": "0.1.8", + "version": "0.2.0", "description": "Typeforge: headless OpenAPI to TypeScript codegen CLI and HTTPFetch runtime", "homepage": "https://github.com/openmirai/typeforge#readme", "bugs": { @@ -12,11 +12,9 @@ "url": "git+https://github.com/openmirai/typeforge.git" }, "bin": { - "openapi-codegen": "./dist/cli.js", "typeforge": "./dist/cli.js" }, "files": [ - "assets/typeforge-logo.png", "dist" ], "type": "module", @@ -59,6 +57,7 @@ }, "scripts": { "build": "tsdown", + "check:package": "node scripts/check-package.mjs", "typecheck": "tsc -p tsconfig.json --pretty false && tsc -p tsconfig.tools.json --pretty false", "format": "oxfmt --check .", "format:fix": "oxfmt --write .", @@ -69,7 +68,7 @@ "test:coverage": "vitest run --coverage", "prepare": "husky", "release": "release-it", - "verify": "pnpm format && pnpm lint && pnpm typecheck && pnpm build && pnpm test:coverage" + "verify": "pnpm format && pnpm lint && pnpm typecheck && pnpm build && pnpm check:package && pnpm test:coverage" }, "dependencies": { "chalk": "5.6.2" diff --git a/scripts/check-package.mjs b/scripts/check-package.mjs new file mode 100644 index 0000000..874c279 --- /dev/null +++ b/scripts/check-package.mjs @@ -0,0 +1,47 @@ +import { execFileSync } from "node:child_process"; + +const output = execFileSync( + "npm", + ["pack", "--dry-run", "--json", "--ignore-scripts"], + { encoding: "utf8" } +); +const [pack] = JSON.parse(output); + +if (pack === undefined) { + throw new Error("npm pack did not return package metadata"); +} + +const paths = pack.files.map((file) => file.path); +const unexpected = paths.filter( + (path) => + path !== "README.md" && path !== "package.json" && !path.startsWith("dist/") +); +const sourceMaps = paths.filter((path) => path.endsWith(".map")); + +if (unexpected.length > 0) { + throw new Error(`Unexpected package files: ${unexpected.join(", ")}`); +} +if (sourceMaps.length > 0) { + throw new Error( + `Source maps must not be published: ${sourceMaps.join(", ")}` + ); +} +if (pack.entryCount > 20) { + throw new Error( + `Package contains ${pack.entryCount} files; expected at most 20` + ); +} +if (pack.size > 75_000) { + throw new Error( + `Package tarball is ${pack.size} bytes; expected at most 75000` + ); +} +if (pack.unpackedSize > 125_000) { + throw new Error( + `Package is ${pack.unpackedSize} unpacked bytes; expected at most 125000` + ); +} + +process.stdout.write( + `typeforge package: ${pack.size} packed bytes, ${pack.unpackedSize} unpacked bytes, ${pack.entryCount} files\n` +); diff --git a/src/config/__tests__/load.test.ts b/src/config/__tests__/load.test.ts index 7a95317..ffb98ce 100644 --- a/src/config/__tests__/load.test.ts +++ b/src/config/__tests__/load.test.ts @@ -15,7 +15,7 @@ function sortedStrings(values: Array): Array { } describe("config/load", () => { - it("prefers typeforge.json over legacy and package config", () => { + it("prefers typeforge.json over package config", () => { const cwd = join( process.cwd(), "test/fixtures/layouts", @@ -27,11 +27,6 @@ describe("config/load", () => { JSON.stringify({ apiRoot: "packages/utils/src/api" }), "utf8" ); - writeFileSync( - join(cwd, "openapi-codegen.json"), - JSON.stringify({ apiRoot: "legacy" }), - "utf8" - ); writeFileSync( join(cwd, "package.json"), JSON.stringify({ typeforge: { apiRoot: "ignored" } }), @@ -41,34 +36,6 @@ describe("config/load", () => { expect(loadProjectConfig(cwd).apiRoot).toBe("packages/utils/src/api"); }); - it("reads legacy project config during migration", () => { - const jsonCwd = join( - process.cwd(), - "test/fixtures/layouts", - `config-legacy-json-${Date.now()}` - ); - mkdirSync(jsonCwd, { recursive: true }); - writeFileSync( - join(jsonCwd, "openapi-codegen.json"), - JSON.stringify({ apiRoot: "legacy/json" }), - "utf8" - ); - expect(loadProjectConfig(jsonCwd).apiRoot).toBe("legacy/json"); - - const packageCwd = join( - process.cwd(), - "test/fixtures/layouts", - `config-legacy-package-${Date.now()}` - ); - mkdirSync(packageCwd, { recursive: true }); - writeFileSync( - join(packageCwd, "package.json"), - JSON.stringify({ openapiCodegen: { apiRoot: "legacy/package" } }), - "utf8" - ); - expect(loadProjectConfig(packageCwd).apiRoot).toBe("legacy/package"); - }); - it("parses source.ts config fields", () => { const cwd = join( process.cwd(), diff --git a/src/config/load.ts b/src/config/load.ts index 0e4b657..44e329d 100644 --- a/src/config/load.ts +++ b/src/config/load.ts @@ -33,7 +33,7 @@ function readPackageConfig(path: string): TypeforgeConfig { try { const raw = readJsonObject(readFileSync(path, "utf8")); - const typeforge = raw["typeforge"] ?? raw["openapiCodegen"]; + const typeforge = raw["typeforge"]; if ( typeof typeforge !== "object" || typeforge === null || @@ -201,15 +201,10 @@ function parseQueryExtends(content: string): QueryExtendsConfig | undefined { export function loadProjectConfig(cwd: string): TypeforgeConfig { const fromJson = readOptionalJson(resolve(cwd, "typeforge.json")); - const fromLegacyJson = readOptionalJson(resolve(cwd, "openapi-codegen.json")); const fromPackage = readPackageConfig(resolve(cwd, "package.json")); return { - apiRoot: - fromJson.apiRoot ?? - fromLegacyJson.apiRoot ?? - fromPackage.apiRoot ?? - DEFAULT_API_ROOT, + apiRoot: fromJson.apiRoot ?? fromPackage.apiRoot ?? DEFAULT_API_ROOT, }; } diff --git a/src/config/types.ts b/src/config/types.ts index 3757bb0..76890c1 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -2,9 +2,6 @@ export interface TypeforgeConfig { apiRoot?: string; } -/** @deprecated Use `TypeforgeConfig`. */ -export type OpenApiCodegenConfig = TypeforgeConfig; - export type GenerationMode = "authoritative" | "merge"; export type NamingStrategy = "path" | "operationId"; diff --git a/src/index.ts b/src/index.ts index 5d686bc..37a41ff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -34,7 +34,6 @@ export { defineSourceConfig } from "./config/define"; export type { GenerationMode, NamingStrategy, - OpenApiCodegenConfig, TypeforgeConfig, QueryExtendsConfig, SourceConfig, diff --git a/src/init/index.ts b/src/init/index.ts index 6f422e7..0720655 100644 --- a/src/init/index.ts +++ b/src/init/index.ts @@ -156,12 +156,10 @@ export function initProject(options: InitOptions): { const cwd = options.cwd ?? process.cwd(); const layout = options.layout ?? "monolith"; const configPath = resolve(cwd, "typeforge.json"); - const legacyConfigPath = resolve(cwd, "openapi-codegen.json"); const projectConfig = loadProjectConfig(cwd); - const apiRoot = - existsSync(configPath) || existsSync(legacyConfigPath) - ? (projectConfig.apiRoot ?? DEFAULT_API_ROOT) - : defaultApiRoot(layout); + const apiRoot = existsSync(configPath) + ? (projectConfig.apiRoot ?? DEFAULT_API_ROOT) + : defaultApiRoot(layout); const apiRootPath = resolve(cwd, apiRoot); const sourceDir = join(apiRootPath, options.sourceKey); @@ -200,7 +198,7 @@ export function initProject(options: InitOptions): { } const configWritePath = resolve(cwd, "typeforge.json"); - if (!existsSync(configWritePath) && !existsSync(legacyConfigPath)) { + if (!existsSync(configWritePath)) { const config: TypeforgeConfig = { apiRoot }; writeFileSync( configWritePath, diff --git a/src/parser/loader.ts b/src/parser/loader.ts index 38b1c13..0ec4302 100644 --- a/src/parser/loader.ts +++ b/src/parser/loader.ts @@ -74,13 +74,7 @@ export function resolveSpecSource( } // 4. Local override file - const defaultLocalPath = "./typeforge.local.json"; - const legacyLocalPath = "./openapi-codegen.local.json"; - const localPath = - opts.localOverridePath ?? - (existsSync(defaultLocalPath) || !existsSync(legacyLocalPath) - ? defaultLocalPath - : legacyLocalPath); + const localPath = opts.localOverridePath ?? "./typeforge.local.json"; if (existsSync(localPath)) { try { const localData = readJsonObject(readFileSync(localPath, "utf8")), diff --git a/test/e2e/cli.test.ts b/test/e2e/cli.test.ts index f52b331..9948fce 100644 --- a/test/e2e/cli.test.ts +++ b/test/e2e/cli.test.ts @@ -79,29 +79,6 @@ describe("e2e cli init", () => { "// keep me" ); }); - - it("uses legacy project config without writing a duplicate", () => { - const root = join(fixtureRoot, "layouts", `cli-init-legacy-${Date.now()}`); - tempRoots.push(root); - mkdirSync(root, { recursive: true }); - writeFileSync( - join(root, "openapi-codegen.json"), - JSON.stringify({ apiRoot: "legacy/api" }), - "utf8" - ); - - const result = runCli(root, [ - "init", - "--source", - "atlas", - "--client", - "fetch", - ]); - - expect(result.exitCode).toBe(0); - expect(existsSync(join(root, "legacy/api/http.ts"))).toBe(true); - expect(existsSync(join(root, "typeforge.json"))).toBe(false); - }); }); describe("e2e cli generate", () => { diff --git a/tsdown.config.ts b/tsdown.config.ts index 42bd404..df88198 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -4,13 +4,12 @@ const shared = { deps: { neverBundle: true, }, - dts: { - sourcemap: true, - }, + dts: true, fixedExtension: false, format: "esm", + minify: true, outDir: "dist", - sourcemap: true, + sourcemap: false, target: "es2022", treeshake: true, tsconfig: "tsconfig.json",