create-grist-widget 0.2.18 → 0.2.19

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.
@@ -50,6 +50,21 @@ for (const [file, subs] of edits) {
50
50
  writeFileSync(p, content)
51
51
  }
52
52
 
53
+ // Record which create-grist-widget release produced this scaffold -- purely
54
+ // informational (shown in a small landing-page footer), never read by the
55
+ // deploy pipeline's own version/release logic. Every scaffold gets this,
56
+ // including the ones the template-canary workflow generates for its `dev`
57
+ // and `canary/latest` branches -- so it survives even if that content is
58
+ // later promoted into a reference repo's `main` and copied again via
59
+ // GitHub's "Use this template".
60
+ const cliPkg = JSON.parse(
61
+ readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf8"),
62
+ )
63
+ const pkgJsonPath = path.join(target, "package.json")
64
+ const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf8"))
65
+ pkgJson.createGristWidgetVersion = cliPkg.version
66
+ writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2) + "\n")
67
+
53
68
  // Initialize git on `main` with the scaffold as its first commit, and a
54
69
  // `dev` branch ready to push for live-preview later. Doing this here (not
55
70
  // leaving it to whoever/whatever runs next) is what actually prevents the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-grist-widget",
3
- "version": "0.2.18",
3
+ "version": "0.2.19",
4
4
  "description": "Scaffold a new Grist custom widget from the grist-widget-sdk Vite template",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -4,7 +4,7 @@ Base template to build a Grist widget with `grist-widget-sdk`.
4
4
 
5
5
  `src/main.tsx` wraps the app with `GristWidgetProvider`, `GristBoundary`, and `GristSdkAlerts`. The latter maps `getGristSdkAlertDescriptors()` from the SDK to shadcn `Alert` (`src/components/grist-sdk-alerts.tsx` + `src/components/ui/alert.tsx`); keep them in sync with the playground when you change alert styling.
6
6
 
7
- Opened outside a Grist iframe, `main.tsx` picks between two components purely by URL shape (`src/lib/showcase-routing.ts`, no router needed): a bare path with no recognized channel suffix renders `TemplateLanding` (its own hero, onboarding, and released-version index); `/latest/`, `/dev/`, or `/v<version>/` renders `ChannelNotice` instead — a distinct hero explaining which build this is, chips to jump to any other version/channel (`src/lib/showcase-versions.ts`), and a copy-this-URL helper for pasting into Grist's custom widget field.
7
+ Opened outside a Grist iframe, `main.tsx` picks between two components purely by URL shape (`src/lib/showcase-routing.ts`, no router needed): a bare path with no recognized channel suffix renders `TemplateLanding` (its own hero, onboarding, and released-version index); `/latest/`, `/dev/`, or `/v<version>/` renders `ChannelNotice` instead — a distinct hero explaining which build this is, chips to jump to any other version/channel (`src/lib/showcase-versions.ts`), and a copy-this-URL helper for pasting into Grist's custom widget field. Both render `ScaffoldFooter` (`src/components/scaffold-footer.tsx`) at the bottom — a small, purely informational note ("Scaffolded from `create-grist-widget@X.Y.Z`") built from `package.json`'s `createGristWidgetVersion` field, which the CLI stamps on every real scaffold (`vite.config.ts` injects it as `__CREATE_GRIST_WIDGET_VERSION__`). Renders nothing if that field is absent, e.g. this pristine template source itself.
8
8
 
9
9
  When actually embedded, `GristStatusChip` (`src/components/grist-status-chip.tsx`) shows a small pill with live handshake status — connecting, retrying with a countdown, connected, or unavailable — using `useAmbientGristHandshake()` from `grist-widget-sdk/advanced`. It must be mounted *inside* `GristWidgetProvider` (see `src/main.tsx`): it only observes that provider's own handshake manager and never mounts a second one, so it's purely observational and can never duplicate or race the real handshake (see `apps/docs/api/handshake.md`).
10
10
 
@@ -89,18 +89,22 @@ automatically.
89
89
  > ⚠️ **Used GitHub's "Use this template" button instead of `npm create
90
90
  > grist-widget`?** That path copies this repo's default branch verbatim —
91
91
  > it doesn't run any of the CLI's own setup (renaming `package.json`,
92
- > titles, etc.), and it won't reset `package.json`'s `version` back to a
93
- > fresh `0.0.1` for you. Do both by hand before your first real release.
94
- > If you also checked **"Include all branches"** (to get `gh-pages`/Pages
95
- > already set up, no manual Settings step), your first real release
96
- > automatically clears out every leftover `v<version>/` it finds — a
97
- > `v<version>/` only counts as a genuine prior release once it carries a
98
- > `showcase-meta.json` naming *this* repo (written by this pipeline's own
99
- > `place` step), so on a repo with no genuine releases yet, everything
100
- > else is provably inherited noise and gets safely wiped on your first
101
- > push to `main`. That cleanup only ever runs before your *first* genuine
102
- > release — once one exists, it's permanently a no-op, so it's still safest
103
- > to never manually re-seed `gh-pages` again after that point.
92
+ > titles, etc.), and whatever version happens to be checked into the
93
+ > source repo's `package.json` (which can be well past `0.0.1` — see below)
94
+ > comes along with it. You don't need to reset it by hand: your first real
95
+ > release always resolves to `v0.0.1` regardless of what `package.json`
96
+ > says, and that value stays untouched in the file — only the deploy
97
+ > pipeline's own idea of "which version is this" is overridden, once, for
98
+ > that first release. If you also checked **"Include all branches"** (to
99
+ > get `gh-pages`/Pages already set up, no manual Settings step), your first
100
+ > real release also automatically clears out every leftover `v<version>/`
101
+ > it finds — a `v<version>/` only counts as a genuine prior release once it
102
+ > carries a `showcase-meta.json` naming *this* repo (written by this
103
+ > pipeline's own `place` step), so on a repo with no genuine releases yet,
104
+ > everything else is provably inherited noise and gets safely wiped on your
105
+ > first push to `main`. Both of these only ever apply before your *first*
106
+ > genuine release — once one exists, they're permanently a no-op, so it's
107
+ > still safest to never manually re-seed `gh-pages` again after that point.
104
108
 
105
109
  **One-time setup** (the workflow can't do this part for you):
106
110
 
@@ -80,6 +80,16 @@ function isGenuineRelease(versionDir, repo) {
80
80
  }
81
81
  }
82
82
 
83
+ /**
84
+ * Whether this repo has ever genuinely published a release (a v<version>/
85
+ * dir whose showcase-meta.json names this repo). Shared by `plan()` (forces
86
+ * the first release to 0.0.1) and `cleanupForeignVersionsIfFirstRelease()`
87
+ * (clears inherited v<version>/ noise) -- both need the exact same signal.
88
+ */
89
+ function hasGenuineRelease(siteDir, repo) {
90
+ return buildVersionsManifest(siteDir, repo).length > 0
91
+ }
92
+
83
93
  /**
84
94
  * Build the release/dev plan from this repo's own package.json + the current
85
95
  * gh-pages tree. Release is skipped when its immutable v<version> dir was
@@ -92,7 +102,14 @@ export function plan(siteDir, repo, event, ref, { force = false } = {}) {
92
102
  return { context: "dev", base: basePathFor(repo, "dev") }
93
103
  }
94
104
  const pkg = JSON.parse(readFileSync("package.json", "utf8"))
95
- const version = pkg.version
105
+ // A repo's first genuine release always starts at 0.0.1, regardless of
106
+ // whatever package.json's version field says -- found live: a repo copied
107
+ // via GitHub's "Use this template" inherited "0.2.18" from the source
108
+ // template repo's own main branch (itself a canary-stamped scaffold that
109
+ // had been promoted there), instead of starting fresh. Once a repo has a
110
+ // genuine release, this no longer applies -- its own version bumps are
111
+ // its own responsibility from then on, same as `cleanupForeignVersionsIfFirstRelease`.
112
+ const version = hasGenuineRelease(siteDir, repo) ? pkg.version : "0.0.1"
96
113
  if (!version) throw new Error("package.json has no version")
97
114
  const versionDir = join(siteDir, `v${version}`)
98
115
  const exists = isGenuineRelease(versionDir, repo)
@@ -190,7 +207,7 @@ function replaceDir(dest, srcDist) {
190
207
  * permanently a no-op.
191
208
  */
192
209
  export function cleanupForeignVersionsIfFirstRelease(siteDir, repo) {
193
- if (buildVersionsManifest(siteDir, repo).length > 0) return { cleaned: [] }
210
+ if (hasGenuineRelease(siteDir, repo)) return { cleaned: [] }
194
211
  const cleaned = []
195
212
  for (const name of existsSync(siteDir) ? readdirSync(siteDir) : []) {
196
213
  if (!/^v\d+\.\d+\.\d+/.test(name)) continue
@@ -1,5 +1,6 @@
1
1
  import { useState } from "react"
2
2
 
3
+ import { ScaffoldFooter } from "@/components/scaffold-footer"
3
4
  import { Button } from "@/components/ui/button"
4
5
  import {
5
6
  Card,
@@ -117,6 +118,8 @@ export function ChannelNotice({
117
118
  >
118
119
  ← Back to the template home
119
120
  </a>
121
+
122
+ <ScaffoldFooter />
120
123
  </div>
121
124
  )
122
125
  }
@@ -0,0 +1,17 @@
1
+ // Shown on the showcase hub and channel pages, never inside Grist (there's
2
+ // no room, and it's not relevant to using the widget). Purely informational:
3
+ // which create-grist-widget release this repo's package.json was scaffolded
4
+ // from -- distinct from the repo's own `version`, which the deploy pipeline
5
+ // always starts fresh at 0.0.1 regardless of this value (see
6
+ // scripts/deploy.mjs's `plan()`). Renders nothing for the pristine template
7
+ // source itself, or any copy predating this field.
8
+ export function ScaffoldFooter() {
9
+ if (!__CREATE_GRIST_WIDGET_VERSION__) return null
10
+
11
+ return (
12
+ <p className="mt-10 text-center text-xs text-muted-foreground">
13
+ Scaffolded from{" "}
14
+ <code className="text-foreground">create-grist-widget@{__CREATE_GRIST_WIDGET_VERSION__}</code>
15
+ </p>
16
+ )
17
+ }
@@ -1,5 +1,6 @@
1
1
  import { useState } from "react"
2
2
 
3
+ import { ScaffoldFooter } from "@/components/scaffold-footer"
3
4
  import { Button } from "@/components/ui/button"
4
5
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
5
6
  import { devUrl, formatDate, useVersions, versionUrl } from "@/lib/showcase-versions"
@@ -301,6 +302,8 @@ git push origin dev`}</code>
301
302
  </ul>
302
303
  )}
303
304
  </section>
305
+
306
+ <ScaffoldFooter />
304
307
  </div>
305
308
  )
306
309
  }
@@ -0,0 +1,6 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ // Injected by vite.config.ts's `define` from package.json's
4
+ // createGristWidgetVersion field -- null for the pristine template source
5
+ // itself (that field only exists on scaffolded copies).
6
+ declare const __CREATE_GRIST_WIDGET_VERSION__: string | null
@@ -1,8 +1,14 @@
1
+ import { readFileSync } from "fs"
1
2
  import path from "path"
2
3
  import tailwindcss from "@tailwindcss/vite"
3
4
  import react from "@vitejs/plugin-react"
4
5
  import { defineConfig } from "vite"
5
6
 
7
+ // Read here (Node context) rather than importing package.json into src/ --
8
+ // keeps the whole file out of the bundle, exposing only the one field the
9
+ // landing page footer needs.
10
+ const pkg = JSON.parse(readFileSync(path.resolve(__dirname, "package.json"), "utf8"))
11
+
6
12
  // https://vite.dev/config/
7
13
  export default defineConfig({
8
14
  plugins: [react(), tailwindcss()],
@@ -11,4 +17,7 @@ export default defineConfig({
11
17
  "@": path.resolve(__dirname, "./src"),
12
18
  },
13
19
  },
20
+ define: {
21
+ __CREATE_GRIST_WIDGET_VERSION__: JSON.stringify(pkg.createGristWidgetVersion ?? null),
22
+ },
14
23
  })