Skip to content

fix: replace Vite ?raw imports with readFileSync for Node.js compatibility#250

Open
Dubworx wants to merge 2 commits intoOpenCoworkAI:mainfrom
Dubworx:fix/headless-esm-compat
Open

fix: replace Vite ?raw imports with readFileSync for Node.js compatibility#250
Dubworx wants to merge 2 commits intoOpenCoworkAI:mainfrom
Dubworx:fix/headless-esm-compat

Conversation

@Dubworx
Copy link
Copy Markdown

@Dubworx Dubworx commented Apr 27, 2026

Summary

  • Replaces Vite-specific ?raw imports in packages/core/src/frames/index.ts and packages/core/src/design-skills/index.ts with fs.readFileSync, enabling @open-codesign/core to be imported from Node.js/tsx contexts (e.g. headless CLI scripts) without Vite running
  • Adds "type": "module" to root package.json to support ESM top-level await in scripts that import from the core package

Problem

The ?raw import suffix is a Vite-only feature. When importing @open-codesign/core from a plain Node.js/tsx script (such as the headless scripts/clone-from-dna.ts or scripts/extract-dna.ts), the import chain fails with:

SyntaxError: The requested module './android.jsx?raw' does not provide an export named 'default'

This blocks any headless/CLI usage of the core generate() function outside of the Vite dev server or Electron app context.

Approach

Replaced all ?raw imports with a readFileSync helper that resolves paths relative to import.meta.url. This works in both:

  • Vite context (Electron app / dev server) — Vite's Node.js compat handles readFileSync fine
  • Plain Node.js/tsx — standard fs module, no bundler needed

Files Changed

File Change
package.json Added "type": "module"
packages/core/src/frames/index.ts ?rawreadFileSync for 5 frame JSX files
packages/core/src/design-skills/index.ts ?rawreadFileSync for 12 skill JSX files

Test plan

  • pnpm dev — Electron app starts and renders designs normally
  • pnpm build — Production build succeeds
  • npx tsx scripts/clone-from-dna.ts --help — CLI script imports core without errors
  • npx tsx scripts/extract-dna.ts --help — CLI script imports core without errors
  • Frame templates and design skills load correctly in both contexts

🤖 Generated with Claude Code

vdcdxxs and others added 2 commits April 27, 2026 12:20
Signed-off-by: hqhq1025 <97931168+hqhq1025@users.noreply.github.com>
(cherry picked from commit 0cbb792)
…ility

The `?raw` import suffix is a Vite-specific feature that fails when
importing @open-codesign/core from Node.js/tsx contexts (e.g. headless
CLI scripts). This replaces all ?raw imports in frames/index.ts and
design-skills/index.ts with fs.readFileSync, which works in both Vite
(via plugin) and plain Node.js.

Also adds "type": "module" to root package.json to enable ESM top-level
await in scripts that import from the core package.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added docs Documentation area:core packages/core (generation orchestration) labels Apr 27, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [Major] Runtime readFileSync() here breaks packaged desktop builds. FRAME_TEMPLATES and DESIGN_SKILLS are imported by the Electron main process (apps/desktop/src/main/index.ts:9, apps/desktop/src/main/index.ts:296), but packaging only ships out/** plus package.json (apps/desktop/electron-builder.yml:15). Replacing ?raw with readFileSync(resolve(__dirname, name)) (packages/core/src/frames/index.ts:19, packages/core/src/design-skills/index.ts:17) means the packaged app now expects the .jsx source files to exist next to the bundled JS, and the current electron-vite build does not emit or copy them (apps/desktop/electron.vite.config.ts:12). That turns a packaged app import into an ENOENT on the first frame/skill load.
    Suggested fix:

    // Keep these starter assets bundled into JS until there is a dual-path
    // solution that also emits/copies the companion .jsx files for packaged builds.
    import iphoneJsx from './iphone.jsx?raw';
    import ipadJsx from './ipad.jsx?raw';
    // ...apply the same revert for the rest of the frame and skill starters
  • [Minor] website/public/llms.txt now points to a plan file that is not in the public checkout. website/public/llms.txt:79 references docs/v0.2-plan.md, but that file is not present, and AGENTS.md:16 explicitly says docs/** is mostly maintainer-local. That leaves public readers and LLM consumers with a dead source reference.
    Suggested fix:

    See the [roadmap](/roadmap) for the milestone plan.

Summary

  • Review mode: initial
  • 2 findings. The main risk is a packaged-desktop regression from swapping bundled starter assets for runtime filesystem reads.
  • Existing tests cover starter contents, but I did not find a regression test for the plain-Node import path claimed in this PR or a packaged Electron smoke path.

Testing

  • Not run (automation). pnpm is not installed in this runner, so I could not execute the repo test suite.

open-codesign Bot

const __dirname = dirname(fileURLToPath(import.meta.url));

function loadFrame(name: string): string {
return readFileSync(resolve(__dirname, name), 'utf-8');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Major] This replaces build-time inlining with a runtime readFileSync, but the packaged desktop app only ships out/** and package.json (apps/desktop/electron-builder.yml:15-17). Because the main process imports FRAME_TEMPLATES / DESIGN_SKILLS (apps/desktop/src/main/index.ts:9-10, apps/desktop/src/main/index.ts:296-300), packaged builds will now look for iphone.jsx-style source files next to the bundled JS and hit ENOENT.

Suggested fix:

import iphoneJsx from './iphone.jsx?raw';
import ipadJsx from './ipad.jsx?raw';
// ...keep the starter bodies bundled until there is a dual-path
// solution that also emits/copies the .jsx assets for packaged builds

Comment thread website/public/llms.txt
- v0.1 migration path — existing SQLite designs migrate into workspaces and session history

Design doc: `docs/plans/2026-04-23-v0.2-agentic-design-loop-design.md` in the repo.
Plan source: `docs/v0.2-plan.md` in the repo.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Minor] docs/v0.2-plan.md is not in the public checkout, and AGENTS.md says docs/** is mostly maintainer-local. This leaves a dead public reference.

Suggested fix:

See the [roadmap](/roadmap) for the milestone plan.

@hqhq1025 hqhq1025 added bot-rerun Temporary label to rerun automation and removed bot-rerun Temporary label to rerun automation labels May 1, 2026
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review mode: initial

Findings

  • [Major] readFileSync approach likely breaks production Electron build
    packages/core/src/frames/index.ts:12-14 and packages/core/src/design-skills/index.ts:12-14 replace Vite's ?raw imports with readFileSync. In Vite, ?raw imports are bundled as inlined strings. The new code relies on node:fs and import.meta.url at runtime.

    • In the development server (pnpm dev), Vite's Node.js compatibility layer may handle readFileSync and resolve paths correctly.
    • In the production Electron build (where the renderer is bundled by Vite), the JSX files will not be bundled alongside the output, and node:fs may not be available in the renderer context unless nodeIntegration is enabled and fs is externalised. This can cause runtime failures when loading frame templates or design skills.
    • No Vite configuration changes are included to externalise node:fs or copy the .jsx files to the output directory.
    • The PR’s test plan checks pnpm dev and pnpm build, but does not verify that the production Electron app can load frames and skills.
      Suggested fix:
    • Before merging, verify the production build with pnpm build and run the Electron app to confirm that frames and design skills render correctly.
    • If the renderer does not have direct filesystem access, an alternative is to keep ?raw imports for the Vite context and provide a Node.js-only entry point (e.g., index.node.ts) that uses readFileSync. The existing @open-codesign/core could then expose both versions via package.json exports or main fields.
    • At minimum, add a Vite plugin to copy the .jsx files into the final build and ensure node:fs is externalised if applicable.
  • [Minor] Unrelated documentation and website changes increase review scope
    The diff touches 13 files, but only 3 (package.json and the two core index.ts files) are related to the described fix. The remaining changes (READMEs, website markdown, FAQ, roadmap, llms.txt, QR code image) appear to be separate v0.2 promotion or maintenance work.
    Suggested fix:
    Split the core compatibility fix into its own PR and handle the documentation updates in a separate PR to keep reviews focused and avoid accidental reverts or conflicts.

  • [Minor] Missing tests for the new file-loading logic
    No unit tests have been added to packages/core to verify that loadFrame and loadSkill work correctly, or that the replaced modules still export the expected arrays. This regression risk is especially relevant when the loading fails (missing file, wrong path) – the current code throws synchronously, which is acceptable, but uncovered paths can hide edge-case bugs.
    Suggested fix:
    Add a Vitest unit test that mocks readFileSync to control file contents and path resolution, and asserts the shape of FRAME_TEMPLATES and DESIGN_SKILLS.

    // packages/core/src/frames/__tests__/index.test.ts
    import { vi, describe, it, expect } from 'vitest';
    vi.mock('node:fs', () => ({ readFileSync: vi.fn((path: string) => `content-of-${path}`) }));
    import { FRAME_TEMPLATES } from '../index';
    
    describe('FRAME_TEMPLATES', () => {
      it('returns the expected frame names', () => {
        expect(FRAME_TEMPLATES.map(([n]) => n)).toEqual([
          'iphone.jsx', 'ipad.jsx', 'watch.jsx', 'android.jsx', 'macos-safari.jsx',
        ]);
      });
    });
  • [Nit] Adding "type": "module" to root package.json may affect non‑TypeScript scripts
    package.json:2 adds "type": "module". The project already uses tsx for Node.js scripts, so ESM behaviour is likely already the default. However, any plain .js file (e.g., in scripts/, husky hooks, or other tooling) that previously relied on CJS will now be treated as ESM.
    Suggested fix:
    Verify that husky, Biome, and other tools run without errors after this change. If any script requires CJS, rename it to .cjs or adjust the "type" field accordingly.

Questions

  • Has the production Electron build (pnpm build followed by launching the packaged app) been tested with this change? The core frames and design skills are critical; a runtime error here would silently break all designs that use device frames or skill starters.
  • Are there any Electron renderer restrictions (contextIsolation, nodeIntegration settings) that could block fs access? If nodeIntegration is off or contextIsolation is true, readFileSync will not work from the renderer side.

Summary
The conversion from Vite‑specific ?raw imports to readFileSync is a valid intent for Node.js/tsx compatibility, but the implementation may break the production Electron build because the new code relies on runtime filesystem access that Vite does not bundle. The missing test coverage and the unrelated documentation changes further reduce confidence. I recommend blocking the merge until the Electron production path is verified and tests are added.

Testing

  • Existing CI should pass pnpm typecheck and pnpm lint, but no new tests were added for the core loading logic.
  • Manually validate the production build by running pnpm build and launching the packaged application, then creating a design that uses a device frame and a design skill.

Open-CoDesign Bot

@hqhq1025 hqhq1025 added bot-rerun Temporary label to rerun automation and removed bot-rerun Temporary label to rerun automation labels May 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core packages/core (generation orchestration) docs Documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants