Skip to content

fix(graphite-to-github-button): compile dist/script.user.js instead of shipping raw TS - #7

Open
nsheaps-oura wants to merge 10 commits into
mainfrom
nate-ai/fix-graphite-to-github-build
Open

fix(graphite-to-github-button): compile dist/script.user.js instead of shipping raw TS#7
nsheaps-oura wants to merge 10 commits into
mainfrom
nate-ai/fix-graphite-to-github-build

Conversation

@nsheaps-oura

Copy link
Copy Markdown
Collaborator

Summary

Commit 4fc2806 ("Rename index.ts to script.user.js") moved raw TypeScript source (with type annotations) straight from packages/graphite-to-github-button/src/index.ts to packages/graphite-to-github-button/dist/script.user.js — it was a pure rename with 0 insertions/deletions, so the file committed under a .js extension was never actually compiled. It's invalid JavaScript that would fail if a userscript manager tried to load it.

  • Restore src/index.ts from the pre-rename content (recovered via git show 4fc2806^:packages/graphite-to-github-button/src/index.ts, confirmed byte-identical to the current broken dist file).
  • Add a real tsc --build step for this package, following the same pattern as sibling packages (github-to-graphite-button, template) in this Nx workspace, plus two narrowly-scoped tsconfig overrides specific to this package:
    • alwaysStrict: false — the file has no import/export, so tsc treats it as a global script and would otherwise prepend "use strict"; above the // ==UserScript== header that Greasy Fork/userscript managers scan for at the top of the file.
    • sourceMap: false — only dist/script.user.js is shipped (via a stable raw.githubusercontent.com URL); skip the sourceMap so the output doesn't end with a dangling //# sourceMappingURL=... reference.
  • Regenerate and force-commit the compiled dist/script.user.js (this path is already tracked despite dist/ being gitignored workspace-wide, from when it was originally force-added).
  • Add .github/workflows/regen-dist.yaml to rebuild and auto-commit dist/script.user.js on every push to main that touches this package's source/config, so this class of bug can't silently recur. Loop-safe via two layers: the paths filter excludes the package's own dist/ output (so the bot's commit can't retrigger it), and the commit step only pushes when the rebuild actually produced a diff.

Verification

  • node --check dist/script.user.js passes — confirmed valid, runnable JS (previously failed with SyntaxError: Unexpected token ':' on the TS type annotations).
  • Manually diffed the compiled output against the previous broken file: type annotations (: HTMLElement, as HTMLElement) are gone, logic is otherwise unchanged.
  • The // ==UserScript== ... // ==/UserScript== header block is still the very first thing in the compiled file (verified this doesn't regress from the alwaysStrict fix above).
  • yarn run build at the repo root builds all 4 Nx projects successfully (nothing else in the workspace broke).
  • oxlint on the restored src/index.ts shows only 3 pre-existing no-useless-escape warnings (0 errors) — these predate the bug and aren't something this PR should silently "fix" as a drive-by.

Open questions for the reviewer

  • Branch protection on main: the new regen-dist.yaml job pushes directly to main using the default GITHUB_TOKEN (job-scoped to contents: write). If main requires PRs/reviews even for bot pushes, this workflow's commit step will fail — please confirm whether that's the case, or whether an app-token bypass (like apply-repo-settings.yaml uses) is needed instead.
  • Userscript header: confirmed it survives the compile step and stays first in the file, but I didn't have a way to verify against Greasy Fork's actual parser/sync behavior end-to-end — worth a sanity check after merge that the sync still picks it up correctly.

Not merging — leaving open for review per request.

Commit 4fc2806 renamed src/index.ts straight to dist/script.user.js
without ever compiling it, leaving raw TypeScript (with type
annotations) committed under a .js extension. Recover the original
TypeScript source at its pre-rename content so it can be compiled
properly again.
Add a real tsc build step, matching the tsc --build convention used
by sibling packages in this Nx workspace. Two per-package tsconfig
overrides are needed beyond the shared pattern:

- alwaysStrict: false — this file has no import/export, so tsc treats
  it as a global script and (since strict implies alwaysStrict) would
  prepend a `"use strict";` line above the `// ==UserScript==` header
  that Greasy Fork/userscript managers scan for at the top of the file.
- sourceMap: false — dist/script.user.js is the only artifact shipped
  (via a stable raw.githubusercontent.com URL) to Greasy Fork; skip the
  sourceMap so the compiled output doesn't end with a dangling
  `//# sourceMappingURL=...` reference to a file nobody serves.

The build script chains the dist/index.js -> dist/script.user.js copy
after `tsc --build` directly (rather than as a separate `postbuild`
script) since Nx invokes the `build` target script directly and does
not honor npm/yarn's pre/post script hook convention.
Replace the raw-TypeScript-with-a-.js-extension file with the actual
tsc build output, so the file Greasy Fork syncs from
raw.githubusercontent.com is valid, runnable JavaScript again.
Add a workflow that rebuilds and commits the compiled userscript on
every push to main touching the package's source or build config, so
the class of bug in 4fc2806 (raw TypeScript committed under a .js
path, silently going stale relative to src/) can't recur.

Loop-safe via two layers: the path filter excludes the package's own
dist/ output, and the commit step only pushes when the rebuild
actually produced a diff. Uses the default GITHUB_TOKEN scoped to
contents: write on this job only, and pinned action commit hashes.
Comment thread .github/workflows/regen-dist.yaml Outdated
main has an active require-pr ruleset with no bypass actors, so the
workflow's plain `git push` to main would always fail once merged. Follow
the same "try direct push, fall back to PR" idiom as
nsheaps/github-actions' sync-main-to-edge reusable workflow: attempt a
direct push first, and only if that's rejected, push to a branch and open
a PR via that repo's open-pr-if-needed composite action.

Addresses review comment: #7 (comment)
nsheaps-oura and others added 2 commits July 24, 2026 14:22
Swap the plain checkout for checkout-as-app so the push authenticates
as the org automation GitHub App, which settings.yml grants an
always-exemption on the require-pr ruleset (same as repo admins).
checkout-as-app also sets git identity to the app's bot identity, so
the manual git config lines are no longer needed. Keep the existing
branch+PR fallback as defense-in-depth per task #45's live-ruleset
sync-gap finding.

Addresses review comment: #7 (comment)

Co-Authored-By: Claude Code (~/src/nsheaps/greasemonkey-scripts) <noreply@anthropic.com>
- tsconfig.base.json: moduleResolution "node" (deprecated alias for
  node10) -> "bundler", the modern pairing for module: ESNext. No
  package imports external modules, so this has no effect on output.
- graphite-to-github-button/tsconfig.json: drop alwaysStrict: false,
  which is being deprecated/removed upstream. Instead, let tsc emit
  its normal "use strict" prologue and strip it in the package's own
  build script when copying dist/index.js to dist/script.user.js, so
  the UserScript header still leads the file regardless of whether a
  future TypeScript keeps an escape hatch for this at all.

Verified locally: a full `nx run-many --target=build --all
--skip-nx-cache` rebuild succeeds across all 4 packages, and
dist/script.user.js comes out byte-identical to what's already
committed.
…cript v6/nx v23/node 24/oxlint v1 toolchain bumps
Merging main (which bumped typescript to ~6.0.3 org-wide) in brought
this package along for the first time on this branch, still carrying
two pre-existing gaps that block a clean build/immutable install:

- package.json still declared typescript ~5.8.3 (this package was
  added later via the ADX-525 merge, after the org-wide v6 bump, and
  never got Renovate's attention). That mismatch against every other
  package's ~6.0.3 left yarn.lock without a consistent resolution, so
  `yarn install --immutable` failed. Bumped to match; yarn.lock
  regenerated.
- tsconfig.json needed an explicit `"types": ["node", "greasemonkey"]`
  override: under this repo's moduleResolution: bundler setting, tsc
  doesn't auto-include @types/node/@types/greasemonkey's ambient
  globals (`module`, `GM`) the way it did under "node".

Same fixes as #23 (opened separately for
main directly); applying them here too since main hadn't merged that
PR yet when this branch picked up the package.
Renamed to regen-userscripts.yaml. Generalizes from hardcoding
graphite-to-github-button as the only package this workflow knows
about, now that this is a real Nx monorepo with multiple packages
(and a 5th, github-actions-grafana-jump, just landed via the ADX-525
merge):

- Build: `yarn run build` (== `nx run-many --target=build --all`, the
  same command ci.yml already runs for every package) instead of the
  hardcoded `yarn workspace @nsheaps/gm-graphite-to-github-button run
  build`. Deliberately not `nx affected` - computing a correct
  base/head for a push-triggered affected run adds real complexity
  (fetch-depth, first-push-to-a-branch edge cases) for a handful of
  packages where a full build is cheap; revisit if that stops being
  true.
- Staging: `find packages -maxdepth 3 -type f -path "*/dist/*.user.js"`
  instead of one hardcoded path. Only packages that actually emit a
  *.user.js artifact are ever staged - packages/_template, template,
  and github-actions-grafana-jump all build fine but produce no
  *.user.js, so they're naturally never touched, no special-casing
  needed. Picks up any future package that adds one automatically.
- paths filter: `packages/**` minus `packages/**/dist/**`, so any
  package's source/config changes trigger a rebuild while the bot's
  own dist-only commits still can't re-trigger it (same loop-safety
  property as before, now repo-wide instead of one package).
- Renamed workflow/job/concurrency-group/branch-fallback/commit-message
  off the single-package naming to reflect the new scope.

Kept unchanged: checkout-as-app auth, the direct-push-to-main with
branch+open-pr-if-needed fallback pattern, task #45's defense-in-depth
reasoning for that fallback.

Verified: `yarn run build` (the workflow's exact build command)
succeeds cleanly for all 5 packages under the current toolchain (TS
6.0.3, nx 23.0.2), and the staging glob matches exactly the one real
*.user.js artifact with zero diff against what's already committed.
nsheaps-oura added a commit that referenced this pull request Jul 27, 2026
## Summary

`main`'s CI has failed on **every push since 2026-07-21** (~10 commits,
including the just-merged ADX-525 Grafana jump buttons feature, #4).
Three independent, compounding causes, all fixed here:

- **`tsconfig.base.json`**: `moduleResolution: "node"` (deprecated alias
for `node10`) is a hard error under TypeScript 6.0.3 (`TS5107`, removed
entirely in 7.0). Switched to `"bundler"`, the modern pairing for
`module: ESNext` — no package in this repo does external imports, so
this has zero effect on compiled output.
- **`packages/github-actions-grafana-jump/tsconfig.json`**: under
`moduleResolution: bundler`, `tsc` no longer auto-includes ambient
globals from `node_modules/@types` the way it did under `"node"`. This
package's `typeof module` test-export guard and `GM.*` API calls need an
explicit `"types": ["node", "greasemonkey"]` to keep resolving.
- **`packages/github-actions-grafana-jump/package.json`**: still
declared `"typescript": "~5.8.3"` — a stale copy from before the
org-wide v6 bump, since this package was only added later via the
ADX-525 PR and never got Renovate's bump. That mismatch against every
other package's `~6.0.3` left `yarn.lock` without a consistent
resolution, so `yarn install --immutable` (what CI actually runs) failed
outright on some runs before ever reaching a TypeScript compile. Bumped
to match; `yarn.lock` regenerated accordingly.

Also cherry-picked `4a38bf0` (from the still-open PR #7) to restore
`packages/graphite-to-github-button/src/index.ts`, which has been
missing from `main` since `4fc2806` renamed it straight to
`dist/script.user.js` without ever compiling it — `tsc --build` for that
package has been failing with "No inputs were found" independent of the
typescript v6 issue, since before the v6 bump even landed.

## Test plan

- [x] `yarn install --immutable` succeeds (this is what CI runs, and was
failing before this change)
- [x] `nx run-many --target=build --all --skip-nx-cache` succeeds for
all 5 packages, cache bypassed
- [x] `github-actions-grafana-jump`'s existing 14 unit tests pass
- [x] Confirmed `packages/graphite-to-github-button/dist/script.user.js`
(the already-committed, force-added artifact) is untouched by this
change — bringing it back in sync with the now-restored `src/index.ts`
is left to PR #7, which already does that plus its own separate
regen-dist workflow generalization
- [ ] CI green on this PR (will confirm once checks run)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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