Skip to content

chore(esm): Convert the structure package to ESM only#1391

Merged
Tobbe merged 8 commits intomainfrom
tobbe-structure-esm
Mar 17, 2026
Merged

chore(esm): Convert the structure package to ESM only#1391
Tobbe merged 8 commits intomainfrom
tobbe-structure-esm

Conversation

@Tobbe
Copy link
Member

@Tobbe Tobbe commented Mar 17, 2026

This was the last package that still used Babel for building

@netlify
Copy link

netlify bot commented Mar 17, 2026

Deploy Preview for cedarjs canceled.

Name Link
🔨 Latest commit 03f07d3
🔍 Latest deploy log https://app.netlify.com/projects/cedarjs/deploys/69b9d450c49696000862ab0b

@Tobbe Tobbe changed the title Tobbe structure esm chore(esm): Convert the structure package to ESM only Mar 17, 2026
@Tobbe Tobbe added this to the chore milestone Mar 17, 2026
@Tobbe Tobbe added the release:chore This PR is a chore (means nothing for users) label Mar 17, 2026
@nx-cloud
Copy link

nx-cloud bot commented Mar 17, 2026

🤖 Nx Cloud AI Fix

Ensure the fix-ci command is configured to always run in your CI pipeline to get automatic fixes in future runs. For more information, please see https://nx.dev/ci/features/self-healing-ci


View your CI Pipeline Execution ↗ for commit 03f07d3

Command Status Duration Result
nx run-many -t build:pack --exclude create-ceda... ✅ Succeeded 8s View ↗
nx run-many -t build ✅ Succeeded 7s View ↗
nx run-many -t test --minWorkers=1 --maxWorkers=4 ✅ Succeeded 5s View ↗
nx run-many -t test:types ✅ Succeeded 10s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-17 22:45:36 UTC

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 17, 2026

Greptile Summary

This PR completes the ESM migration of the @cedarjs/structure package — the last package in the monorepo still using Babel for its build. It switches to pure TypeScript compilation (tsc), adds "type": "module" to package.json, adds .js extensions to all relative imports, and replaces the two decorator dependencies (lazy-get-decorator and lodash-decorators) with lightweight inline implementations.

Key changes worth noting:

  • Custom lazy/memo decorators replace external libraries. The lazy implementation is functionally equivalent for runtime use but is missing configurable: true on the cached own property (a subtle divergence from the original library that may affect test spies).
  • createRequire workaround is used to load @prisma/internals, which is a CJS-only bundle incompatible with Node's ESM static named-export analysis — this is the correct approach and is well-documented with a comment.
  • Babel enum workaround removed in check.ts: DiagnosticSeverity no longer needs to be imported via .default, so both symbols are now cleanly destructured in a single import.
  • tsconfig.json overrides emitDeclarationOnly: false from the base config so TypeScript emits both JS and .d.ts files — the correct setup when Babel is no longer in the pipeline.
  • package.json exports expose several internal ./dist/model/* sub-paths, which couples the public API to the internal build directory structure.

Confidence Score: 4/5

  • Safe to merge with minor follow-up items on the custom decorator implementation and package exports.
  • The core ESM conversion (import extensions, tsconfig, package.json type:module) is mechanically straightforward and correct. The CJS interop for @prisma/internals is handled well. The main concerns are: (1) the lazy decorator drops configurable: true from the original library which could break test spies, and (2) internal dist/ paths are exposed in the exports map. Neither is a runtime blocker for the primary use case, but the decorator difference is a subtle behavioural regression worth fixing before shipping.
  • packages/structure/src/x/decorators.ts deserves the most attention — the custom lazy and memo implementations replace external libraries and the configurable regression could affect downstream test infrastructure.

Important Files Changed

Filename Overview
packages/structure/src/x/decorators.ts Replaces lazy-get-decorator and lodash-decorators with custom lazy and memo implementations. The lazy decorator is missing configurable: true on the cached property (regression from original library), and __memoCache is assigned as an enumerable own property.
packages/structure/package.json Adds "type": "module", new exports map, removes Babel/lodash-decorators/lazy-get-decorator dependencies. The exports map exposes internal ./dist/ paths directly, tying the public API to the build output directory.
packages/structure/tsconfig.json Switches from declaration-only emit (Babel did JS) to full emit. Uses module: "node20" with moduleResolution: "node16" which is a minor inconsistency; they should ideally be the same version.
packages/structure/src/model/RWProject.ts Uses createRequire to load @prisma/internals (CJS-only bundle) reliably from ESM context. Constructor no longer takes projectRoot; getPaths() is called directly. Clean change with good code comment explaining the CJS workaround.
packages/structure/src/nodes.ts Adds .js extensions to all relative imports. Updates @memo(JSON.stringify) to @memo((...args) => JSON.stringify(args)) — needed because the previous form passed JSON.stringify as a variadic callback directly, which incorrectly received arguments like (value, replacer, space) from the internal serializer call.
packages/cli/src/commands/check.ts Removes the workaround for the Babel enum issue where DiagnosticSeverity had to be imported via .default. Now both printDiagnostics and DiagnosticSeverity are cleanly destructured from the same import.
packages/cli/src/telemetry/resource.js Removes projectRoot: getPaths().base from new RWProject() — aligns with the updated constructor that reads paths directly via getPaths() internally.
packages/structure/src/model/RWSDL.ts Migrates GraphQL imports from deep sub-paths (graphql/language/parser) to top-level graphql package, and updates import extensions to .js.
packages/structure/src/model/RWSDLField.ts Migrates GraphQL type imports from deep sub-paths (graphql/language/ast) to top-level graphql package, and updates relative import extensions to .js.
packages/structure/.babelrc.js Deleted — Babel configuration is no longer needed now that TypeScript handles the full build.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["tsc -p tsconfig.json\n(module: node20, moduleResolution: node16)"]
    A --> B["dist/*.js\n(ESM output)"]
    A --> C["dist/*.d.ts\n(Type declarations)"]

    subgraph pkg ["@cedarjs/structure package.json"]
        E["exports: '.' → dist/index.js"]
        F["exports: './dist/model/RWProject'"]
        G["exports: './dist/model/RWRoute'"]
        H["exports: './dist/model/RWPage.js'"]
    end

    B --> pkg
    C --> pkg

    subgraph esm ["ESM Source (src/)"]
        I["index.ts\n(exports DiagnosticSeverity, RWProject, RWRoute)"]
        J["model/RWProject.ts\n(createRequire for @prisma/internals)"]
        K["x/decorators.ts\n(custom lazy + memo)"]
        L["model/*.ts\n(.js import extensions)"]
    end

    M["@prisma/internals\n(CJS-only)"] -->|createRequire| J
    K -->|"@lazy() getters"| L
    K -->|"@memo() methods"| L
    I --> J
    J --> L
Loading

Last reviewed commit: 1acc046

@Tobbe
Copy link
Member Author

Tobbe commented Mar 17, 2026

@greptileai Please do a full review again

@Tobbe Tobbe enabled auto-merge (squash) March 17, 2026 22:23
@Tobbe Tobbe merged commit b859a70 into main Mar 17, 2026
41 checks passed
@Tobbe Tobbe deleted the tobbe-structure-esm branch March 17, 2026 22:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release:chore This PR is a chore (means nothing for users)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant