create-grist-widget 0.2.22 → 0.2.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-grist-widget",
3
- "version": "0.2.22",
3
+ "version": "0.2.24",
4
4
  "description": "Scaffold a new Grist custom widget from the grist-widget-sdk Vite template",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -5,12 +5,21 @@ name: Deploy widget to GitHub Pages
5
5
  # /<repo>/v<version>/ + mutable /<repo>/latest/.
6
6
  # Rebuilt only when package.json's version has no v<version> dir yet, so
7
7
  # re-pushing main without a version bump is a no-op deploy.
8
- # A push that *creates* the main ref (github.event.created) -- fired by
9
- # GitHub's "Use this template" copying this repo onto a brand-new ref --
10
- # is skipped entirely instead: it would otherwise publish whatever
11
- # boilerplate version package.json happens to have checked in and
12
- # permanently spend the first-release override before anyone's done
13
- # real work.
8
+ # A push that *creates* the main ref, authored by GitHub's own web-flow
9
+ # bot -- fired by "Use this template" copying this repo onto a brand-new
10
+ # ref -- is skipped entirely instead: it would otherwise publish
11
+ # whatever boilerplate version package.json happens to have checked in
12
+ # and permanently spend the first-release override before anyone's done
13
+ # real work. (Checking github.event.created alone isn't enough: a CLI
14
+ # scaffold's own first `git push -u origin main` also creates that ref
15
+ # for the first time, but is a real user's genuine first release and
16
+ # must not be skipped -- the web-flow committer check tells the two
17
+ # apart.) That run still clears any foreign content the copy brought
18
+ # along (stray v<version>/ dirs, a root/latest/ still pointing at the
19
+ # source repo's own path) via `cleanup-foreign`, and places the freshly
20
+ # built showcase hub at the site root via `place-root`, so the bare
21
+ # repo-root URL works immediately instead of 404ing until the widget's
22
+ # real first release. It just never creates a v<version>/ or latest/.
14
23
  # - push to dev -> dev channel: mutable /<repo>/dev/
15
24
  # with version.json + a self-reload snippet, for live review inside Grist.
16
25
  #
@@ -98,7 +107,7 @@ jobs:
98
107
  --event "${{ github.event_name }}" \
99
108
  --ref "${{ github.ref }}" \
100
109
  ${{ github.event.inputs.force == 'true' && '--force' || '' }} \
101
- ${{ github.event.created == true && '--created' || '' }})
110
+ ${{ github.event.created == true && github.event.head_commit.committer.email == 'noreply@github.com' && '--created' || '' }})
102
111
  echo "Plan: $PLAN"
103
112
  node -e '
104
113
  const p = JSON.parse(process.argv[1]);
@@ -106,13 +115,17 @@ jobs:
106
115
  const out = process.env.GITHUB_OUTPUT;
107
116
  fs.appendFileSync(out, `context=${p.context}\n`);
108
117
  fs.appendFileSync(out, `skip=${p.skip ? "true" : "false"}\n`);
118
+ fs.appendFileSync(out, `reason=${p.reason || ""}\n`);
109
119
  fs.appendFileSync(out, `version=${p.version || ""}\n`);
110
120
  fs.appendFileSync(out, `base=${p.base}\n`);
111
121
  fs.appendFileSync(out, `firstRelease=${p.firstRelease ? "true" : "false"}\n`);
112
122
  ' "$PLAN"
113
123
 
124
+ # Also builds on the ref-created push (skip == 'true' there), but with
125
+ # the root base plan() hands back for that case (/<repo>/, not a
126
+ # versioned path) -- see the "landing hub" step below.
114
127
  - name: Build
115
- if: steps.plan.outputs.skip != 'true'
128
+ if: steps.plan.outputs.skip != 'true' || steps.plan.outputs.reason == 'ref-created'
116
129
  run: |
117
130
  pnpm exec tsc -b
118
131
  pnpm exec vite build --base "${{ steps.plan.outputs.base }}"
@@ -130,9 +143,28 @@ jobs:
130
143
  --sha "$SHA" --ref "${{ github.ref }}"
131
144
 
132
145
  - name: Skip (already published)
133
- if: steps.plan.outputs.skip == 'true'
146
+ if: steps.plan.outputs.skip == 'true' && steps.plan.outputs.reason != 'ref-created'
134
147
  run: echo "== skip — v${{ steps.plan.outputs.version }} already published"
135
148
 
149
+ # The ref-created "Use this template" push publishes no versioned
150
+ # release (that would spend the first-release override on unmodified
151
+ # boilerplate -- see plan()), so it never reaches Place. But it still:
152
+ # 1. clears any foreign content the copy brought along
153
+ # (cleanupForeignVersionsIfFirstRelease, normally a side effect of
154
+ # Place) -- stray v<version>/ dirs, a root/latest/ pointing at the
155
+ # source repo's own path; then
156
+ # 2. places the freshly built showcase hub at the site root, so
157
+ # https://owner.github.io/<repo>/ isn't a bare 404 until the
158
+ # widget's first real release (the CLI path gets a working root on
159
+ # its own genuine first push; this makes the template-copy path
160
+ # match). No v<version>/ or latest/ -- those stay absent, correctly
161
+ # 404ing, until a real release exists.
162
+ - name: Clean foreign content + place landing hub (ref-created push)
163
+ if: steps.plan.outputs.reason == 'ref-created'
164
+ run: |
165
+ node scripts/deploy.mjs cleanup-foreign --site site --repo "$REPO_NAME"
166
+ node scripts/deploy.mjs place-root --site site --dist dist
167
+
136
168
  - name: Finalize (versions.json + commit + push)
137
169
  run: |
138
170
  UPDATE_VERSIONS=""
@@ -114,10 +114,18 @@ automatically.
114
114
  >
115
115
  > "Use this template" also immediately fires this workflow once on its own,
116
116
  > the moment the repo is created — before you've cloned it, let alone
117
- > changed anything. That run is recognized (GitHub marks it as the push
118
- > that *created* the `main` ref) and skipped entirely: nothing gets
119
- > published, and it doesn't count as your first release, so the `0.0.1`
120
- > override above still applies in full once you actually push real changes.
117
+ > changed anything. That run is recognized (GitHub's own web-flow bot
118
+ > creating the `main` ref) and **publishes no versioned release**: it
119
+ > doesn't count as your first release, so the `0.0.1` override above still
120
+ > applies in full once you actually push real changes. It does two useful
121
+ > things, though: if you checked "Include all branches", it clears out
122
+ > whatever foreign content came along (stray `v<version>/` dirs, a
123
+ > root/`latest/` still pointing at the *source* repo's own path); and it
124
+ > places the landing page at your repo-root URL so
125
+ > `https://<you>.github.io/<repo>/` works right away, instead of 404ing
126
+ > until your first real release. Your `/latest/` and `/v<version>/` URLs
127
+ > stay absent (they 404) until you actually publish — there's no release to
128
+ > point them at yet.
121
129
 
122
130
  **One-time setup** (the workflow can't do this part for you):
123
131
 
@@ -23,14 +23,29 @@
23
23
  // Prints JSON { context: "release"|"dev", version?, base, skip }.
24
24
  // Release is skipped when v<version> already exists on gh-pages
25
25
  // (idempotence: re-pushing main without a version bump is a no-op).
26
- // --created (github.event.created off a push event) additionally skips
27
- // unconditionally, without consuming the first-release version override
28
- // -- see plan()'s own comment.
26
+ // --created additionally skips unconditionally, without consuming the
27
+ // first-release version override -- see plan()'s own comment. Callers
28
+ // must only pass --created for GitHub's own server-side "Use this
29
+ // template" push, never a real user push that merely happens to create
30
+ // a ref for the first time (e.g. a CLI scaffold's first `git push -u
31
+ // origin main`) -- see deploy.yml's wiring for the distinguishing check.
29
32
  //
30
33
  // place --site <dir> --repo <name> --channel <release|dev>
31
34
  // [--version <v>] --dist <dir> --sha <sha> [--ref <ref>]
32
35
  // Copies a freshly built dist into the gh-pages tree.
33
36
  //
37
+ // cleanup-foreign --site <dir> --repo <name>
38
+ // Standalone entry point for `cleanupForeignVersionsIfFirstRelease` --
39
+ // used on a skipped ref-created push (see `plan`), which never reaches
40
+ // `place` (the only other caller of this cleanup) so foreign content
41
+ // from "Use this template" would otherwise sit untouched indefinitely.
42
+ //
43
+ // place-root --site <dir> --dist <dir>
44
+ // Place a root-based build (built with base "/<repo>/") at the site
45
+ // root only -- the showcase hub landing page. Used on the ref-created
46
+ // push so the bare repo-root URL works before the first real release,
47
+ // without publishing a versioned release. Run after `cleanup-foreign`.
48
+ //
34
49
  // remove --site <dir>
35
50
  // Removes dev/ from the gh-pages tree (retire the dev URL when the `dev`
36
51
  // branch is deleted). Never touches a release or latest/.
@@ -134,10 +149,21 @@ export function plan(siteDir, repo, event, ref, { force = false, created = false
134
149
  // gh-pages) keeps hasGenuineRelease() false, so the override survives
135
150
  // intact for whenever a real release actually happens.
136
151
  if (created && event === "push") {
152
+ // Publish no versioned release (so the first-release override above
153
+ // survives), but the caller still builds + places the showcase hub at
154
+ // the site root -- otherwise https://owner.github.io/<repo>/ is a bare
155
+ // 404 until the widget's first real release (found live on a fresh "Use
156
+ // this template" copy). The hub renders for any URL with no channel
157
+ // suffix, the bare root included (see main.tsx), so it must be built
158
+ // based at the repo root (/<repo>/), not a versioned path -- hence the
159
+ // root base here rather than basePathFor's v<version>/. The workflow
160
+ // keys off reason === "ref-created" to build with this base and place
161
+ // root-only. `skip` stays true so the normal versioned place/finalize
162
+ // path is bypassed.
137
163
  return {
138
164
  context: "release",
139
165
  version,
140
- base: basePathFor(repo, "release", version),
166
+ base: `/${repo}/`,
141
167
  skip: true,
142
168
  reason: "ref-created",
143
169
  firstRelease,
@@ -237,21 +263,28 @@ function replaceDir(dest, srcDist) {
237
263
 
238
264
  /**
239
265
  * If this repo has never genuinely published a release before (no
240
- * v<version>/ directory whose showcase-meta.json matches this repo), any
241
- * v<version>/ directories present are provably inherited noise -- e.g. from
242
- * copying this template repo including its gh-pages branch (GitHub's "Use
243
- * this template" -> "Include all branches"). Safe to clear them all before
244
- * placing this repo's actual first release: there is no ambiguity, since a
245
- * repo with zero genuine releases can't have any of its own history to lose.
246
- * Never touches dev/, latest/, or site-root files. Once a genuine release
247
- * exists for this repo, buildVersionsManifest is non-empty and this is
248
- * permanently a no-op.
266
+ * v<version>/ directory whose showcase-meta.json matches this repo), *every*
267
+ * top-level entry except `dev/` (the mutable dev channel, legitimately this
268
+ * repo's own regardless of release history) and a small set of
269
+ * pipeline-managed files is provably inherited noise -- e.g. from copying
270
+ * this template repo including its gh-pages branch (GitHub's "Use this
271
+ * template" -> "Include all branches"): stray `v<version>/` directories,
272
+ * but also a root `index.html`/`latest/` still referencing the *source*
273
+ * repo's own base path (found live: a fresh copy's root and `latest/`
274
+ * pointed at `/grist-widget-template/v0.2.22/assets/...`, 404ing on this
275
+ * repo's own Pages site -- a blank page -- because the ref-created push
276
+ * that used to overwrite them with this repo's own build is now skipped
277
+ * entirely, see `plan()`). Safe to clear unconditionally: a repo with zero
278
+ * genuine releases can't have any of its own history to lose. Once a
279
+ * genuine release exists for this repo, buildVersionsManifest is non-empty
280
+ * and this is permanently a no-op.
249
281
  */
250
282
  export function cleanupForeignVersionsIfFirstRelease(siteDir, repo) {
251
283
  if (hasGenuineRelease(siteDir, repo)) return { cleaned: [] }
284
+ const PROTECTED = new Set([".nojekyll", "dev", "versions.json", ".git"])
252
285
  const cleaned = []
253
286
  for (const name of existsSync(siteDir) ? readdirSync(siteDir) : []) {
254
- if (!/^v\d+\.\d+\.\d+/.test(name)) continue
287
+ if (PROTECTED.has(name)) continue
255
288
  rmSync(join(siteDir, name), { recursive: true, force: true })
256
289
  cleaned.push(name)
257
290
  }
@@ -297,6 +330,27 @@ export function placeTarget({ siteDir, channel, version, distDir, sha, ref, repo
297
330
  return { placed: `v${version}`, latest: "latest", root: true }
298
331
  }
299
332
 
333
+ /**
334
+ * Place a root-based build (built with base "/<repo>/") at the gh-pages site
335
+ * root only -- the showcase hub landing page, nothing else. Used on the
336
+ * ref-created "Use this template" push (see plan()): publishes no versioned
337
+ * release, so the first-release override survives, but keeps the bare
338
+ * repo-root URL from 404ing until that first real release (found live: a
339
+ * fresh copy's https://owner.github.io/<repo>/ was a 404, since the initial
340
+ * deploy that used to place this hub now publishes nothing). Never creates a
341
+ * v<version>/ or latest/. On the first genuine release, placeTarget's own
342
+ * cleanupForeignVersionsIfFirstRelease clears this hub (root index.html +
343
+ * assets/) before placing the release's own root copy, so nothing lingers.
344
+ * Callers run `cleanup-foreign` first, so a re-triggered ref-created run
345
+ * replaces the hub cleanly rather than layering onto stale files.
346
+ */
347
+ export function placeRootHub({ siteDir, distDir }) {
348
+ if (!existsSync(distDir)) throw new Error(`dist not found: ${distDir}`)
349
+ mkdirSync(siteDir, { recursive: true })
350
+ cpSync(distDir, siteDir, { recursive: true })
351
+ return { placed: "root" }
352
+ }
353
+
300
354
  /**
301
355
  * Remove the mutable dev dir from the gh-pages tree (retire the dev URL when
302
356
  * the `dev` branch is deleted). Only ever touches `dev/` — never a versioned
@@ -432,6 +486,16 @@ function main() {
432
486
  console.log("remove:", JSON.stringify(res))
433
487
  break
434
488
  }
489
+ case "cleanup-foreign": {
490
+ const res = cleanupForeignVersionsIfFirstRelease(args.site, args.repo)
491
+ console.log("cleanup-foreign:", JSON.stringify(res))
492
+ break
493
+ }
494
+ case "place-root": {
495
+ const res = placeRootHub({ siteDir: args.site, distDir: args.dist })
496
+ console.log("place-root:", JSON.stringify(res))
497
+ break
498
+ }
435
499
  case "finalize": {
436
500
  const res = finalize({
437
501
  siteDir: args.site,