Skip to content

Fix building the @moq/publish and @moq/watch packages#957

Merged
kixelated merged 12 commits intomainfrom
hang-ui-types
Feb 14, 2026
Merged

Fix building the @moq/publish and @moq/watch packages#957
kixelated merged 12 commits intomainfrom
hang-ui-types

Conversation

@kixelated
Copy link
Collaborator

@kixelated kixelated commented Feb 13, 2026

  1. AudioWorklets were not resolved at runtime. There's now a script to compile and inline them. In the long-term, we should serve stuff as public assets but there's no need right this moment.
  2. Types were not being published.

@kixelated kixelated enabled auto-merge (squash) February 13, 2026 18:39
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 13, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This pull request reorganizes the monorepo's build infrastructure and introduces a new AudioWorklet handling system. Changes include: consolidating shared build scripts from js/scripts/ to js/common/, introducing a new Vite plugin (workletInline) for compiling and inlining AudioWorklets as blob URLs, updating TypeScript configurations to set rootDir to ./src with declaration-only emission, modifying build scripts across multiple packages to reference the new common script locations, adding publint for dist-level package validation, adding new library entry points (index and element) to Vite configurations, and inserting an explicit build step into the CI pipeline.

🚥 Pre-merge checks | ✅ 1 | ❌ 3
❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Merge Conflict Detection ⚠️ Warning ❌ Merge conflicts detected (21 files):

⚔️ bun.lock (content)
⚔️ js/hang-demo/vite.config.ts (content)
⚔️ js/hang/package.json (content)
⚔️ js/lite/package.json (content)
⚔️ js/publish/package.json (content)
⚔️ js/publish/src/audio/encoder.ts (content)
⚔️ js/publish/tsconfig.json (content)
⚔️ js/publish/vite.config.ts (content)
⚔️ js/signals/package.json (content)
⚔️ js/token/package.json (content)
⚔️ js/tsconfig.json (content)
⚔️ js/ui-core/package.json (content)
⚔️ js/ui-core/tsconfig.json (content)
⚔️ js/watch/package.json (content)
⚔️ js/watch/src/audio/decoder.ts (content)
⚔️ js/watch/tsconfig.json (content)
⚔️ js/watch/vite.config.ts (content)
⚔️ justfile (content)
⚔️ package.json (content)
⚔️ rs/moq-native/src/client.rs (content)
⚔️ rs/moq-native/src/server.rs (content)

These conflicts must be resolved before merging into main.
Resolve conflicts locally and push changes to this branch.
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to verify if the description relates to the changeset. Add a description explaining the changes, such as the rationale for moving scripts to ../common, adding the worklet plugin, and updating TypeScript configurations.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The PR title describes the main objective of fixing the @moq/publish and @moq/watch package builds, which aligns with the extensive build configuration and script changes across multiple package files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hang-ui-types

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
js/hang-ui/package.json (1)

8-11: ⚠️ Potential issue | 🟠 Major

Missing types condition in exports — the package.ts build script does not auto-inject it.

The PR adds tsc to emit .d.ts files into dist/, but the exports map in package.json lacks a types condition. While package.ts rewrites path values (transforming ./src/publish/index.tsx to ./publish/index.js), it does not generate missing types conditions. TypeScript consumers using moduleResolution: "bundler" or "node16" won't resolve the generated declarations.

Update exports to include the types condition:

"exports": {
  "./publish": {
    "types": "./dist/publish/index.d.ts",
    "default": "./publish/index.js"
  },
  "./watch": {
    "types": "./dist/watch/index.d.ts",
    "default": "./watch/index.js"
  }
}

The package.ts script will automatically rewrite the nested default paths during the build.

@kixelated kixelated disabled auto-merge February 13, 2026 18:45
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@js/scripts/package.ts`:
- Around line 28-33: The object-export branch in js/scripts/package.ts
incorrectly rewrites every string sub-value with .js; update the loop that
iterates over sub-keys of val so that when the key name is exactly "types" you
call rewritePath(val[sub], "d.ts") (or otherwise ensure the types path is
rewritten to a .d.ts extension), and for all other string sub-values continue
using rewritePath(val[sub], "js"); keep the existing typeof checks (if (typeof
val[sub] === "string")) and only change the argument passed to rewritePath based
on the sub key name.
🧹 Nitpick comments (1)
js/scripts/package.ts (1)

56-69: Workspace dependency resolution assumes a flat sibling layout.

Line 63 builds ../${packageDir}/package.json which works for the current js/* workspace structure. However, if a scoped package name doesn't match its directory (e.g., @moq/hang-ui living in js/hang-ui), the name.split("/")[1] extraction of hang-ui still works because the workspace directories match the unscoped portion.

One edge-case: if readFileSync fails (e.g., the workspace package doesn't exist at the expected path), the error message won't be very helpful. Consider wrapping with a try/catch that names the dependency.

💡 Optional: improve error diagnostics
 		if (typeof version === "string" && version.startsWith("workspace:")) {
 			const packageDir = name.includes("/") ? name.split("/")[1] : name;
 			const workspacePkgPath = `../${packageDir}/package.json`;
-			const workspacePkg = JSON.parse(readFileSync(workspacePkgPath, "utf8"));
+			let workspacePkg: { version: string };
+			try {
+				workspacePkg = JSON.parse(readFileSync(workspacePkgPath, "utf8"));
+			} catch (e) {
+				throw new Error(`Failed to resolve workspace dependency "${name}" at ${workspacePkgPath}: ${e}`);
+			}
 			dependencies[name] = `^${workspacePkg.version}`;

@kixelated kixelated enabled auto-merge (squash) February 13, 2026 19:05
@kixelated kixelated disabled auto-merge February 13, 2026 19:06
@kixelated
Copy link
Collaborator Author

Blocked on #948

kixelated and others added 4 commits February 13, 2026 11:47
Update watch and publish packages to use emitDeclarationOnly tsc
after vite build, matching the ui-core pattern. Remove unnecessary
tsconfig.build.json and src/ui/tsconfig.json files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kixelated kixelated changed the title Publish types for hang-ui Fix how the @moq/publish and @moq/watch packages are built Feb 13, 2026
@kixelated kixelated changed the title Fix how the @moq/publish and @moq/watch packages are built Fix building the @moq/publish and @moq/watch packages Feb 13, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@js/common/vite-plugin-worklet.ts`:
- Around line 30-50: The load(id) handler in vite-plugin-worklet.ts doesn’t
register the original worklet source with Vite’s watcher, so edits won’t trigger
rebuilds; inside the async load(id) function (where filePath is derived from
id.slice(0, -SUFFIX.length)) call this.addWatchFile(filePath) (guarded by
checking this.addWatchFile exists) before invoking build so Vite tracks the
original file in dev/watch mode and rebuilds the virtual module when the source
changes.
🧹 Nitpick comments (2)
js/common/vite-plugin-worklet.ts (1)

43-43: Add a guard for missing esbuild output.

If esbuild produces no output files (e.g., empty entry or misconfiguration), result.outputFiles[0].text will throw an opaque TypeError. A quick guard with a descriptive error improves debuggability.

Proposed fix
-		const compiled = result.outputFiles[0].text;
+		const output = result.outputFiles?.[0];
+		if (!output) {
+			throw new Error(`worklet-inline: esbuild produced no output for ${filePath}`);
+		}
+		const compiled = output.text;
js/common/package.ts (1)

93-106: Consider adjusting publint level to filter message types at source rather than in code.

With level: "suggestion", publint returns suggestions, warnings, and errors (the level option sets a minimum threshold). Since any non-empty messages array triggers process.exit(1), even benign suggestions fail the build. If this strictness is intentional, that's fine — but if suggestions become noise, the simplest fix is to use level: "warning" instead, which filters out suggestions at the API level rather than through post-processing. This avoids the asymmetry of logging suggestions but only exiting on errors/warnings.

Comment on lines 30 to 50
async load(id) {
if (!id.endsWith(SUFFIX)) return;

const filePath = id.slice(0, -SUFFIX.length);

const result = await build({
entryPoints: [filePath],
bundle: true,
write: false,
format: "esm",
target: "esnext",
});

const compiled = result.outputFiles[0].text;

return [
`const code = ${JSON.stringify(compiled)};`,
`const blob = new Blob([code], { type: "application/javascript" });`,
`export default URL.createObjectURL(blob);`,
].join("\n");
},
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing this.addWatchFile — worklet source changes won't trigger rebuilds in dev mode.

Since the worklet file is resolved to a virtual module (id + SUFFIX), Vite doesn't track the original source file for changes. In watch/dev mode, edits to the worklet won't trigger a rebuild.

Proposed fix
 		async load(id) {
 			if (!id.endsWith(SUFFIX)) return;
 
 			const filePath = id.slice(0, -SUFFIX.length);
+			this.addWatchFile(filePath);
 
 			const result = await build({
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async load(id) {
if (!id.endsWith(SUFFIX)) return;
const filePath = id.slice(0, -SUFFIX.length);
const result = await build({
entryPoints: [filePath],
bundle: true,
write: false,
format: "esm",
target: "esnext",
});
const compiled = result.outputFiles[0].text;
return [
`const code = ${JSON.stringify(compiled)};`,
`const blob = new Blob([code], { type: "application/javascript" });`,
`export default URL.createObjectURL(blob);`,
].join("\n");
},
async load(id) {
if (!id.endsWith(SUFFIX)) return;
const filePath = id.slice(0, -SUFFIX.length);
this.addWatchFile(filePath);
const result = await build({
entryPoints: [filePath],
bundle: true,
write: false,
format: "esm",
target: "esnext",
});
const compiled = result.outputFiles[0].text;
return [
`const code = ${JSON.stringify(compiled)};`,
`const blob = new Blob([code], { type: "application/javascript" });`,
`export default URL.createObjectURL(blob);`,
].join("\n");
},
🤖 Prompt for AI Agents
In `@js/common/vite-plugin-worklet.ts` around lines 30 - 50, The load(id) handler
in vite-plugin-worklet.ts doesn’t register the original worklet source with
Vite’s watcher, so edits won’t trigger rebuilds; inside the async load(id)
function (where filePath is derived from id.slice(0, -SUFFIX.length)) call
this.addWatchFile(filePath) (guarded by checking this.addWatchFile exists)
before invoking build so Vite tracks the original file in dev/watch mode and
rebuilds the virtual module when the source changes.

kixelated and others added 2 commits February 13, 2026 15:05
- Include worklet.d.ts in hang-demo tsconfig so ?worklet imports resolve
- Add addWatchFile in vite-plugin-worklet so edits trigger rebuilds
- Change publint level from "suggestion" to "warning"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kixelated kixelated requested a review from jdreetz February 14, 2026 00:13
if (val.endsWith(".css")) {
// CSS exports are only needed for dev-time resolution;
// consumers inline them at build time via @import.
// We purposely do not copy them to the dist to help catch bugs.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Eventually, we should roll them up and serve them out of public instead of inline.


// Lint the package to catch publishing issues
console.log("🔍 Running publint...");
const { messages, pkg: lintPkg } = await publint({
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Catches bugs with the generated package.json file. ex. It was pointing to non-existent css files.

* then inlined as a string. At runtime, a blob URL is created and exported.
* Pass the URL to audioWorklet.addModule().
*/
export function workletInline(): Plugin {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Eventually, we should serve this out of public instead.

"rootDir": ".",
"outDir": "dist"
"rootDir": "./src",
"jsx": "preserve",
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I didn't mean to make this change (claude pls) but I guess it's fine to have these JSX settings for the entire module.

… plugin

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Collaborator

@jdreetz jdreetz left a comment

Choose a reason for hiding this comment

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

Seems fine to me.

@kixelated kixelated merged commit 48e884a into main Feb 14, 2026
1 check passed
@kixelated kixelated deleted the hang-ui-types branch February 14, 2026 21:58
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.

2 participants