Skip to content

Generate package.json and .js imports when transpile is enabled - #21

Closed
kac-axelor wants to merge 4 commits into
axelor:devfrom
kac-axelor:transpile-to-package
Closed

kac-axelor wants to merge 4 commits into
axelor:devfrom
kac-axelor:transpile-to-package

Conversation

@kac-axelor

Copy link
Copy Markdown
Contributor

Why

When a Next.js app consumes the goovee-generated client, the bundler mangles the generated code, breaking entity class names and TypeORM metadata. The current workaround in consumer apps is to build with next build --no-mangling, which disables mangling globally — overkill for a problem that only affects one set of files.

The cleaner fix is to put the generated code somewhere the bundler treats as an external package, e.g. via Next.js serverExternalPackages. That requires the output to look like a real installable package: it must live under a resolvable name and ship a valid package.json.

This PR makes that possible without forcing every consumer to hand-write a package.json next to their generated files.

What changed

When schema.transpile is truthy, the generator now:

  1. Writes a package.json into outDir describing the generated module — name, version, type, and exports for ./models and ./client. type is derived from transpile.module (commonjs → commonjs, otherwise module), so ESM/CJS output stays consistent.

  2. Emits .js extensions on relative import specifiers. Node's ESM resolver requires explicit extensions and does not perform directory-index resolution; the .js form also works under CJS. Without this, the transpiled output cannot run under Node directly.

  3. Imports ../models/index instead of ../models in the client entry point, for the same Node-ESM reason.

A new transpile.packageName option lets users override the default package name (@goovee/generated).

What did not change

When schema.transpile is unset or false, behavior is identical to before this PR:
raw .ts files are emitted to outDir with extensionless relative imports, and no package.json is written. Existing users who don't opt into transpile see no difference.

Consumer setup

In goovee.config.json:

{
  "schema": {
    "outDir": "node_modules/@goovee/generated",
    "transpile": true
  }
}

In next.config.mjs:

const nextConfig = {
  serverExternalPackages: ["@goovee/generated"],
  // ...
};

To use a different package name:

{
  "schema": {
    "outDir": "node_modules/@myorg/db-client",
    "transpile": { "packageName": "@myorg/db-client" }
  }
}

Monorepo setup

repo/
├─ apps/
│  └─ web/
├─ packages/
│  └─ db/
└─ pnpm-workspace.yaml

In goovee.config.json:

{
  "schema": {
    "outDir": "packages/db",
    "transpile": { "packageName": "@myorg/db" }
  }
}

In apps/web/package.json:

{
  "dependencies": {
    "@myorg/db": "workspace:*"
  }
}

In apps/web/next.config.mjs:

const nextConfig = {
  serverExternalPackages: ["@myorg/db"],
};

kac-axelor and others added 4 commits May 20, 2026 18:38
…ing (axelor#18)

* Fix schema lookups and repository resolution to be resilient to name mangling

* Fix schema lookups to use the active naming strategy for table name resolution

* Add e2e tests for mangling resilience

Reproduce the bugs PR axelor#18 fixes against a real DataSource and Postgres
container by mutating Function.name on entity classes before TypeORM
metadata is built (the same effect bundlers have in production).

Failing-on-broken-code (regression coverage):

- Top-level isStringField: lowerCase/unaccent normalization is silently
  disabled because the schemaDefs lookup misses when targetName is
  mangled. SELECTs return zero rows instead of normalized matches.

- Relation reference resolved by String field: `title: { select: { name
  "Jose" } }` throws "Referenced X not found" when the inverse entity
  is mangled, because the inner findOne hits the same isStringField bug
  and skips unaccent/lower wrapping.

Passing-as-documentation:

- ManyToOne reference, OneToMany collection-create, and relation-loader
  query under single-class mangling — these paths work incidentally
  because both sides read the mangled Function.name; the
  getRepository(rMeta.target) change is defensive.

- Name collision (two entity classes forced to the same mangled name) —
  still resolves to the correct repository, confirming TypeORM's lookup
  is more resilient than feared.

---------

Co-authored-by: ame-axelor <236853329+ame-axelor@users.noreply.github.com>
Allows the transpiled output to be consumed as a real package
(e.g. marked as a Next.js serverExternalPackage), avoiding
bundler mangling.

- Write package.json into outDir after transpile, with `type`
  derived from transpile.module (commonjs vs module).
- Add transpile.packageName to override the default
  @goovee/generated.
- Append .js to relative import specifiers when transpiling,
  so Node's ESM/CJS resolver can resolve the output without
  bundler help.

Without transpile, behavior is unchanged: raw .ts is emitted
with extensionless imports and no package.json.
@kac-axelor

Copy link
Copy Markdown
Contributor Author

#18 This fixes mangling which got merged , hence this feature is obsolete.

@kac-axelor kac-axelor closed this Jun 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant