create-ncblock 0.0.1
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/README.md +49 -0
- package/bin/cli.js +33 -0
- package/package.json +25 -0
- package/scripts/init.ts +527 -0
- package/scripts/scaffold-assets/AGENTS.md +65 -0
- package/scripts/utils/templates.ts +293 -0
- package/sdk-version.json +1 -0
- package/templates/debug/README.md +36 -0
- package/templates/debug/_gitignore +2 -0
- package/templates/debug/custom_blocks.json +9 -0
- package/templates/debug/dist/assets/index-Cet2SsjS.css +2 -0
- package/templates/debug/dist/assets/index-DAzv_fuh.js +9 -0
- package/templates/debug/dist/custom_blocks.json +9 -0
- package/templates/debug/dist/index.html +16 -0
- package/templates/debug/index.html +15 -0
- package/templates/debug/node_modules/.bin/browserslist +21 -0
- package/templates/debug/node_modules/.bin/esbuild +21 -0
- package/templates/debug/node_modules/.bin/jiti +21 -0
- package/templates/debug/node_modules/.bin/rollup +21 -0
- package/templates/debug/node_modules/.bin/tsc +21 -0
- package/templates/debug/node_modules/.bin/tsserver +21 -0
- package/templates/debug/node_modules/.bin/tsx +21 -0
- package/templates/debug/node_modules/.bin/vite +21 -0
- package/templates/debug/node_modules/.vite/deps/_metadata.json +50 -0
- package/templates/debug/node_modules/.vite/deps/package.json +3 -0
- package/templates/debug/node_modules/.vite/deps/react-CsV5wVHy.js +770 -0
- package/templates/debug/node_modules/.vite/deps/react-CsV5wVHy.js.map +1 -0
- package/templates/debug/node_modules/.vite/deps/react-dom.js +185 -0
- package/templates/debug/node_modules/.vite/deps/react-dom.js.map +1 -0
- package/templates/debug/node_modules/.vite/deps/react-dom_client.js +14384 -0
- package/templates/debug/node_modules/.vite/deps/react-dom_client.js.map +1 -0
- package/templates/debug/node_modules/.vite/deps/react.js +2 -0
- package/templates/debug/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
- package/templates/debug/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
- package/templates/debug/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
- package/templates/debug/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
- package/templates/debug/node_modules/.vite/deps/valibot.js +6623 -0
- package/templates/debug/node_modules/.vite/deps/valibot.js.map +1 -0
- package/templates/debug/node_modules/.vite-temp/vite.config.ts.timestamp-1778623720803-0bcf523a67aa8.mjs +15 -0
- package/templates/debug/package.json +30 -0
- package/templates/debug/src/index.css +62 -0
- package/templates/debug/src/index.tsx +1963 -0
- package/templates/debug/tsconfig.json +17 -0
- package/templates/debug/vite.config.ts +8 -0
- package/templates/empty/README.md +10 -0
- package/templates/empty/_gitignore +2 -0
- package/templates/empty/custom_blocks.json +12 -0
- package/templates/empty/dist/assets/index-CodJADav.js +9 -0
- package/templates/empty/dist/custom_blocks.json +12 -0
- package/templates/empty/dist/index.html +15 -0
- package/templates/empty/index.html +15 -0
- package/templates/empty/node_modules/.bin/esbuild +21 -0
- package/templates/empty/node_modules/.bin/jiti +21 -0
- package/templates/empty/node_modules/.bin/tsc +21 -0
- package/templates/empty/node_modules/.bin/tsserver +21 -0
- package/templates/empty/node_modules/.bin/tsx +21 -0
- package/templates/empty/node_modules/.bin/vite +21 -0
- package/templates/empty/node_modules/.vite/deps/_metadata.json +50 -0
- package/templates/empty/node_modules/.vite/deps/package.json +3 -0
- package/templates/empty/node_modules/.vite/deps/react-CsV5wVHy.js +770 -0
- package/templates/empty/node_modules/.vite/deps/react-CsV5wVHy.js.map +1 -0
- package/templates/empty/node_modules/.vite/deps/react-dom.js +185 -0
- package/templates/empty/node_modules/.vite/deps/react-dom.js.map +1 -0
- package/templates/empty/node_modules/.vite/deps/react-dom_client.js +14384 -0
- package/templates/empty/node_modules/.vite/deps/react-dom_client.js.map +1 -0
- package/templates/empty/node_modules/.vite/deps/react.js +2 -0
- package/templates/empty/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
- package/templates/empty/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
- package/templates/empty/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
- package/templates/empty/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
- package/templates/empty/node_modules/.vite/deps/valibot.js +6623 -0
- package/templates/empty/node_modules/.vite/deps/valibot.js.map +1 -0
- package/templates/empty/package.json +28 -0
- package/templates/empty/src/index.tsx +12 -0
- package/templates/empty/tsconfig.json +17 -0
- package/templates/empty/vite.config.ts +7 -0
- package/templates/gantt-chart/node_modules/.bin/tsc +21 -0
- package/templates/gantt-chart/node_modules/.bin/tsserver +21 -0
- package/templates/gantt-chart/node_modules/.bin/vite +21 -0
- package/templates/gantt-chart/node_modules/.vite/deps/_metadata.json +50 -0
- package/templates/gantt-chart/node_modules/.vite/deps/package.json +3 -0
- package/templates/gantt-chart/node_modules/.vite/deps/react-CsV5wVHy.js +770 -0
- package/templates/gantt-chart/node_modules/.vite/deps/react-CsV5wVHy.js.map +1 -0
- package/templates/gantt-chart/node_modules/.vite/deps/react-dom.js +185 -0
- package/templates/gantt-chart/node_modules/.vite/deps/react-dom.js.map +1 -0
- package/templates/gantt-chart/node_modules/.vite/deps/react-dom_client.js +14384 -0
- package/templates/gantt-chart/node_modules/.vite/deps/react-dom_client.js.map +1 -0
- package/templates/gantt-chart/node_modules/.vite/deps/react.js +2 -0
- package/templates/gantt-chart/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
- package/templates/gantt-chart/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
- package/templates/gantt-chart/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
- package/templates/gantt-chart/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
- package/templates/gantt-chart/node_modules/.vite/deps/valibot.js +6623 -0
- package/templates/gantt-chart/node_modules/.vite/deps/valibot.js.map +1 -0
- package/templates/hello-world/node_modules/.bin/tsc +21 -0
- package/templates/hello-world/node_modules/.bin/tsserver +21 -0
- package/templates/hello-world/node_modules/.bin/vite +21 -0
- package/templates/hello-world/node_modules/.vite/deps/_metadata.json +50 -0
- package/templates/hello-world/node_modules/.vite/deps/package.json +3 -0
- package/templates/hello-world/node_modules/.vite/deps/react-CsV5wVHy.js +770 -0
- package/templates/hello-world/node_modules/.vite/deps/react-CsV5wVHy.js.map +1 -0
- package/templates/hello-world/node_modules/.vite/deps/react-dom.js +185 -0
- package/templates/hello-world/node_modules/.vite/deps/react-dom.js.map +1 -0
- package/templates/hello-world/node_modules/.vite/deps/react-dom_client.js +14384 -0
- package/templates/hello-world/node_modules/.vite/deps/react-dom_client.js.map +1 -0
- package/templates/hello-world/node_modules/.vite/deps/react.js +2 -0
- package/templates/hello-world/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
- package/templates/hello-world/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
- package/templates/hello-world/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
- package/templates/hello-world/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
- package/templates/hello-world/node_modules/.vite/deps/valibot.js +6623 -0
- package/templates/hello-world/node_modules/.vite/deps/valibot.js.map +1 -0
- package/templates/interactive-resize/node_modules/.bin/tsc +21 -0
- package/templates/interactive-resize/node_modules/.bin/tsserver +21 -0
- package/templates/interactive-resize/node_modules/.bin/vite +21 -0
- package/templates/interactive-resize/node_modules/.vite/deps/_metadata.json +50 -0
- package/templates/interactive-resize/node_modules/.vite/deps/package.json +3 -0
- package/templates/interactive-resize/node_modules/.vite/deps/react-CsV5wVHy.js +770 -0
- package/templates/interactive-resize/node_modules/.vite/deps/react-CsV5wVHy.js.map +1 -0
- package/templates/interactive-resize/node_modules/.vite/deps/react-dom.js +185 -0
- package/templates/interactive-resize/node_modules/.vite/deps/react-dom.js.map +1 -0
- package/templates/interactive-resize/node_modules/.vite/deps/react-dom_client.js +14384 -0
- package/templates/interactive-resize/node_modules/.vite/deps/react-dom_client.js.map +1 -0
- package/templates/interactive-resize/node_modules/.vite/deps/react.js +2 -0
- package/templates/interactive-resize/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
- package/templates/interactive-resize/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
- package/templates/interactive-resize/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
- package/templates/interactive-resize/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
- package/templates/interactive-resize/node_modules/.vite/deps/valibot.js +6623 -0
- package/templates/interactive-resize/node_modules/.vite/deps/valibot.js.map +1 -0
- package/templates/org-chart/node_modules/.bin/tsc +21 -0
- package/templates/org-chart/node_modules/.bin/tsserver +21 -0
- package/templates/org-chart/node_modules/.bin/vite +21 -0
- package/templates/org-chart/node_modules/.vite/deps/_metadata.json +50 -0
- package/templates/org-chart/node_modules/.vite/deps/package.json +3 -0
- package/templates/org-chart/node_modules/.vite/deps/react-CsV5wVHy.js +770 -0
- package/templates/org-chart/node_modules/.vite/deps/react-CsV5wVHy.js.map +1 -0
- package/templates/org-chart/node_modules/.vite/deps/react-dom.js +185 -0
- package/templates/org-chart/node_modules/.vite/deps/react-dom.js.map +1 -0
- package/templates/org-chart/node_modules/.vite/deps/react-dom_client.js +14384 -0
- package/templates/org-chart/node_modules/.vite/deps/react-dom_client.js.map +1 -0
- package/templates/org-chart/node_modules/.vite/deps/react.js +2 -0
- package/templates/org-chart/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
- package/templates/org-chart/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
- package/templates/org-chart/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
- package/templates/org-chart/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
- package/templates/org-chart/node_modules/.vite/deps/valibot.js +6623 -0
- package/templates/org-chart/node_modules/.vite/deps/valibot.js.map +1 -0
- package/templates/radar-chart/README.md +55 -0
- package/templates/radar-chart/_gitignore +2 -0
- package/templates/radar-chart/custom_blocks.json +34 -0
- package/templates/radar-chart/dist/assets/index-DOf05oXg.css +2 -0
- package/templates/radar-chart/dist/assets/index-DWpNd1qt.js +9 -0
- package/templates/radar-chart/dist/custom_blocks.json +34 -0
- package/templates/radar-chart/dist/index.html +16 -0
- package/templates/radar-chart/index.html +15 -0
- package/templates/radar-chart/node_modules/.bin/esbuild +21 -0
- package/templates/radar-chart/node_modules/.bin/jiti +21 -0
- package/templates/radar-chart/node_modules/.bin/tsc +21 -0
- package/templates/radar-chart/node_modules/.bin/tsserver +21 -0
- package/templates/radar-chart/node_modules/.bin/tsx +21 -0
- package/templates/radar-chart/node_modules/.bin/vite +21 -0
- package/templates/radar-chart/node_modules/.vite/deps/_metadata.json +50 -0
- package/templates/radar-chart/node_modules/.vite/deps/package.json +3 -0
- package/templates/radar-chart/node_modules/.vite/deps/react-CsV5wVHy.js +770 -0
- package/templates/radar-chart/node_modules/.vite/deps/react-CsV5wVHy.js.map +1 -0
- package/templates/radar-chart/node_modules/.vite/deps/react-dom.js +185 -0
- package/templates/radar-chart/node_modules/.vite/deps/react-dom.js.map +1 -0
- package/templates/radar-chart/node_modules/.vite/deps/react-dom_client.js +14384 -0
- package/templates/radar-chart/node_modules/.vite/deps/react-dom_client.js.map +1 -0
- package/templates/radar-chart/node_modules/.vite/deps/react.js +2 -0
- package/templates/radar-chart/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
- package/templates/radar-chart/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
- package/templates/radar-chart/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
- package/templates/radar-chart/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
- package/templates/radar-chart/node_modules/.vite/deps/valibot.js +6623 -0
- package/templates/radar-chart/node_modules/.vite/deps/valibot.js.map +1 -0
- package/templates/radar-chart/package.json +30 -0
- package/templates/radar-chart/src/index.css +44 -0
- package/templates/radar-chart/src/index.tsx +531 -0
- package/templates/radar-chart/tsconfig.json +17 -0
- package/templates/radar-chart/vite.config.ts +8 -0
- package/templates/table-view/README.md +43 -0
- package/templates/table-view/_gitignore +2 -0
- package/templates/table-view/custom_blocks.json +9 -0
- package/templates/table-view/dist/assets/index-Bd8u_e4X.js +12 -0
- package/templates/table-view/dist/assets/index-BkZn3aQZ.css +1 -0
- package/templates/table-view/dist/custom_blocks.json +9 -0
- package/templates/table-view/dist/index.html +16 -0
- package/templates/table-view/index.html +15 -0
- package/templates/table-view/node_modules/.bin/esbuild +21 -0
- package/templates/table-view/node_modules/.bin/jiti +21 -0
- package/templates/table-view/node_modules/.bin/rollup +21 -0
- package/templates/table-view/node_modules/.bin/tsc +21 -0
- package/templates/table-view/node_modules/.bin/tsserver +21 -0
- package/templates/table-view/node_modules/.bin/tsx +21 -0
- package/templates/table-view/node_modules/.bin/vite +21 -0
- package/templates/table-view/node_modules/.vite/deps/@tanstack_react-table.js +2809 -0
- package/templates/table-view/node_modules/.vite/deps/@tanstack_react-table.js.map +1 -0
- package/templates/table-view/node_modules/.vite/deps/_metadata.json +56 -0
- package/templates/table-view/node_modules/.vite/deps/package.json +3 -0
- package/templates/table-view/node_modules/.vite/deps/react-D5jdVkJj.js +790 -0
- package/templates/table-view/node_modules/.vite/deps/react-D5jdVkJj.js.map +1 -0
- package/templates/table-view/node_modules/.vite/deps/react-dom.js +185 -0
- package/templates/table-view/node_modules/.vite/deps/react-dom.js.map +1 -0
- package/templates/table-view/node_modules/.vite/deps/react-dom_client.js +14384 -0
- package/templates/table-view/node_modules/.vite/deps/react-dom_client.js.map +1 -0
- package/templates/table-view/node_modules/.vite/deps/react.js +2 -0
- package/templates/table-view/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
- package/templates/table-view/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
- package/templates/table-view/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
- package/templates/table-view/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
- package/templates/table-view/node_modules/.vite/deps/valibot.js +6623 -0
- package/templates/table-view/node_modules/.vite/deps/valibot.js.map +1 -0
- package/templates/table-view/package.json +31 -0
- package/templates/table-view/src/index.css +256 -0
- package/templates/table-view/src/index.tsx +1814 -0
- package/templates/table-view/src/table-model.ts +663 -0
- package/templates/table-view/tsconfig.json +17 -0
- package/templates/table-view/vite.config.ts +8 -0
- package/templates/us-heatmap/node_modules/.bin/tsc +21 -0
- package/templates/us-heatmap/node_modules/.bin/tsserver +21 -0
- package/templates/us-heatmap/node_modules/.bin/vite +21 -0
- package/templates/us-heatmap/node_modules/.vite/deps/_metadata.json +50 -0
- package/templates/us-heatmap/node_modules/.vite/deps/package.json +3 -0
- package/templates/us-heatmap/node_modules/.vite/deps/react-CsV5wVHy.js +770 -0
- package/templates/us-heatmap/node_modules/.vite/deps/react-CsV5wVHy.js.map +1 -0
- package/templates/us-heatmap/node_modules/.vite/deps/react-dom.js +185 -0
- package/templates/us-heatmap/node_modules/.vite/deps/react-dom.js.map +1 -0
- package/templates/us-heatmap/node_modules/.vite/deps/react-dom_client.js +14384 -0
- package/templates/us-heatmap/node_modules/.vite/deps/react-dom_client.js.map +1 -0
- package/templates/us-heatmap/node_modules/.vite/deps/react.js +2 -0
- package/templates/us-heatmap/node_modules/.vite/deps/react_jsx-dev-runtime.js +204 -0
- package/templates/us-heatmap/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +1 -0
- package/templates/us-heatmap/node_modules/.vite/deps/react_jsx-runtime.js +208 -0
- package/templates/us-heatmap/node_modules/.vite/deps/react_jsx-runtime.js.map +1 -0
- package/templates/us-heatmap/node_modules/.vite/deps/valibot.js +6623 -0
- package/templates/us-heatmap/node_modules/.vite/deps/valibot.js.map +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# create-ncblock
|
|
2
|
+
|
|
3
|
+
> [!NOTE]
|
|
4
|
+
> Nothing to see here. This is **extreme** alpha and currently only works for an unreleased product.
|
|
5
|
+
|
|
6
|
+
Works with `bun create`, `npm create`, `pnpm create`, and `yarn create`.
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
bun create ncblock my-view
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
No GitHub authentication required — templates are bundled in the package.
|
|
13
|
+
|
|
14
|
+
> **Pre-release.** Breaking changes may land at any time before 1.0.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
bun create ncblock [destination] [options]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Options
|
|
23
|
+
|
|
24
|
+
| Flag | Default | Description |
|
|
25
|
+
| ----------------------- | -------------------- | --------------------------------------------- |
|
|
26
|
+
| `--template` / `-t` | _(interactive)_ | Template to scaffold from. |
|
|
27
|
+
| `--name` / `-n` | template name | Package name for the new project. |
|
|
28
|
+
| `--git` / `--no-git` | _(interactive)_ | Initialize a git repo. |
|
|
29
|
+
| `--install` / `--no-install` | _(interactive)_ | Install dependencies after scaffolding. |
|
|
30
|
+
|
|
31
|
+
The first positional argument is the destination directory. If omitted, defaults to `./<name>`.
|
|
32
|
+
|
|
33
|
+
### Examples
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Interactive — pick a template, name, etc.
|
|
37
|
+
bun create ncblock
|
|
38
|
+
|
|
39
|
+
# Non-interactive — scaffold "empty" template into ./my-view
|
|
40
|
+
bun create ncblock ./my-view --template empty --name my-view --no-git --install
|
|
41
|
+
|
|
42
|
+
# Works with npm and pnpm too
|
|
43
|
+
npm create ncblock ./my-view -- --template empty --no-git --no-install
|
|
44
|
+
pnpm create ncblock ./my-view --template empty --no-git --no-install
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Requirements
|
|
48
|
+
|
|
49
|
+
- Node ≥ 18
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process"
|
|
3
|
+
import { createRequire } from "node:module"
|
|
4
|
+
import { dirname, resolve } from "node:path"
|
|
5
|
+
|
|
6
|
+
const require = createRequire(import.meta.url)
|
|
7
|
+
let tsxBin
|
|
8
|
+
try {
|
|
9
|
+
const tsxPkg = require.resolve("tsx/package.json")
|
|
10
|
+
tsxBin = resolve(dirname(tsxPkg), "dist/cli.mjs")
|
|
11
|
+
} catch {
|
|
12
|
+
process.stderr.write(
|
|
13
|
+
"✗ Could not locate the bundled tsx runtime. Reinstall create-ncblock.\n",
|
|
14
|
+
)
|
|
15
|
+
process.exit(1)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const entryPath = resolve(
|
|
19
|
+
dirname(new URL(import.meta.url).pathname),
|
|
20
|
+
"..",
|
|
21
|
+
"scripts",
|
|
22
|
+
"init.ts",
|
|
23
|
+
)
|
|
24
|
+
const result = spawnSync(
|
|
25
|
+
"node",
|
|
26
|
+
[tsxBin, entryPath, ...process.argv.slice(2)],
|
|
27
|
+
{
|
|
28
|
+
stdio: "inherit",
|
|
29
|
+
env: process.env,
|
|
30
|
+
},
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
process.exit(result.status ?? 1)
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-ncblock",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Create a Notion custom view block project.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-ncblock": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"scripts",
|
|
12
|
+
"templates",
|
|
13
|
+
"sdk-version.json",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"tsx": "^4.21.0"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
}
|
|
25
|
+
}
|
package/scripts/init.ts
ADDED
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import { execSync } from "child_process"
|
|
2
|
+
import { basename, dirname, resolve } from "path"
|
|
3
|
+
import {
|
|
4
|
+
clearScreenDown,
|
|
5
|
+
createInterface,
|
|
6
|
+
cursorTo,
|
|
7
|
+
emitKeypressEvents,
|
|
8
|
+
moveCursor,
|
|
9
|
+
} from "readline"
|
|
10
|
+
import { fileURLToPath } from "url"
|
|
11
|
+
import {
|
|
12
|
+
getTemplateByName,
|
|
13
|
+
getTemplates,
|
|
14
|
+
scaffoldTemplate,
|
|
15
|
+
type TemplateMetadata,
|
|
16
|
+
} from "./utils/templates"
|
|
17
|
+
|
|
18
|
+
function detectPM(): string {
|
|
19
|
+
const ua = process.env.npm_config_user_agent ?? ""
|
|
20
|
+
if (ua.startsWith("pnpm")) {
|
|
21
|
+
return "pnpm"
|
|
22
|
+
}
|
|
23
|
+
if (ua.startsWith("bun")) {
|
|
24
|
+
return "bun"
|
|
25
|
+
}
|
|
26
|
+
if (ua.startsWith("npm")) {
|
|
27
|
+
return "npm"
|
|
28
|
+
}
|
|
29
|
+
for (const pm of ["pnpm", "bun", "npm"]) {
|
|
30
|
+
try {
|
|
31
|
+
execSync(`${pm} --version`, { stdio: "ignore" })
|
|
32
|
+
return pm
|
|
33
|
+
} catch {}
|
|
34
|
+
}
|
|
35
|
+
return "pnpm"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function parseArgs(argv: string[]) {
|
|
39
|
+
const args: Record<string, string | boolean> = {}
|
|
40
|
+
const positional: string[] = []
|
|
41
|
+
let i = 2
|
|
42
|
+
while (i < argv.length) {
|
|
43
|
+
const arg = argv[i]
|
|
44
|
+
if (arg === "--template" || arg === "-t") {
|
|
45
|
+
args.template = argv[++i]
|
|
46
|
+
} else if (arg === "--name" || arg === "-n") {
|
|
47
|
+
args.name = argv[++i]
|
|
48
|
+
} else if (arg === "--git") {
|
|
49
|
+
args.git = true
|
|
50
|
+
} else if (arg === "--no-git") {
|
|
51
|
+
args.git = false
|
|
52
|
+
} else if (arg === "--install") {
|
|
53
|
+
args.install = true
|
|
54
|
+
} else if (arg === "--no-install") {
|
|
55
|
+
args.install = false
|
|
56
|
+
} else if (!arg.startsWith("-")) {
|
|
57
|
+
positional.push(arg)
|
|
58
|
+
}
|
|
59
|
+
i++
|
|
60
|
+
}
|
|
61
|
+
return { args, positional }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const pm = detectPM()
|
|
65
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
66
|
+
const root = resolve(__dirname, "..")
|
|
67
|
+
const templateBaseDir = resolve(root, "templates")
|
|
68
|
+
const templates = getTemplates(templateBaseDir)
|
|
69
|
+
|
|
70
|
+
const c = {
|
|
71
|
+
reset: "\x1b[0m",
|
|
72
|
+
dim: "\x1b[2m",
|
|
73
|
+
bold: "\x1b[1m",
|
|
74
|
+
green: "\x1b[32m",
|
|
75
|
+
cyan: "\x1b[36m",
|
|
76
|
+
yellow: "\x1b[33m",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let rl: ReturnType<typeof createInterface> | undefined
|
|
80
|
+
|
|
81
|
+
function getPromptInterface() {
|
|
82
|
+
rl ??= createInterface({ input: process.stdin, output: process.stdout })
|
|
83
|
+
return rl
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function closePromptInterface() {
|
|
87
|
+
rl?.close()
|
|
88
|
+
rl = undefined
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function question(prompt: string): Promise<string> {
|
|
92
|
+
return new Promise(res => {
|
|
93
|
+
getPromptInterface().question(prompt, res)
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function truncateText(value: string, maxLength: number): string {
|
|
98
|
+
if (maxLength <= 0) {
|
|
99
|
+
return ""
|
|
100
|
+
}
|
|
101
|
+
if (value.length <= maxLength) {
|
|
102
|
+
return value
|
|
103
|
+
}
|
|
104
|
+
if (maxLength === 1) {
|
|
105
|
+
return "…"
|
|
106
|
+
}
|
|
107
|
+
return `${value.slice(0, maxLength - 1)}…`
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function wrapText(value: string, maxWidth: number): string[] {
|
|
111
|
+
if (maxWidth <= 0) {
|
|
112
|
+
return [value]
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const words = value.trim().split(/\s+/).filter(Boolean)
|
|
116
|
+
if (words.length === 0) {
|
|
117
|
+
return [""]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const lines: string[] = []
|
|
121
|
+
let currentLine = ""
|
|
122
|
+
|
|
123
|
+
for (const word of words) {
|
|
124
|
+
if (word.length > maxWidth) {
|
|
125
|
+
if (currentLine) {
|
|
126
|
+
lines.push(currentLine)
|
|
127
|
+
currentLine = ""
|
|
128
|
+
}
|
|
129
|
+
let remaining = word
|
|
130
|
+
while (remaining.length > maxWidth) {
|
|
131
|
+
lines.push(remaining.slice(0, maxWidth - 1) + "…")
|
|
132
|
+
remaining = remaining.slice(maxWidth - 1)
|
|
133
|
+
}
|
|
134
|
+
currentLine = remaining
|
|
135
|
+
continue
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const nextLine = currentLine ? `${currentLine} ${word}` : word
|
|
139
|
+
if (nextLine.length <= maxWidth) {
|
|
140
|
+
currentLine = nextLine
|
|
141
|
+
continue
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
lines.push(currentLine)
|
|
145
|
+
currentLine = word
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (currentLine) {
|
|
149
|
+
lines.push(currentLine)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return lines
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function validateProjectName(value: string): string | undefined {
|
|
156
|
+
if (value.trim().length === 0) {
|
|
157
|
+
return "Project name cannot be empty."
|
|
158
|
+
}
|
|
159
|
+
if (!/^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(value)) {
|
|
160
|
+
return "Use a valid package name: lowercase, no spaces."
|
|
161
|
+
}
|
|
162
|
+
return undefined
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function validateLocation(value: string): string | undefined {
|
|
166
|
+
return value.trim().length > 0 ? undefined : "Location cannot be empty."
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async function ask(
|
|
170
|
+
label: string,
|
|
171
|
+
fallback: string,
|
|
172
|
+
override?: string,
|
|
173
|
+
validate?: (value: string) => string | undefined,
|
|
174
|
+
): Promise<string> {
|
|
175
|
+
if (override !== undefined) {
|
|
176
|
+
return override
|
|
177
|
+
}
|
|
178
|
+
while (true) {
|
|
179
|
+
const answer = (
|
|
180
|
+
await question(
|
|
181
|
+
` ${c.cyan}${label}${c.reset} ${c.dim}(${fallback})${c.reset} `,
|
|
182
|
+
)
|
|
183
|
+
).trim()
|
|
184
|
+
const value = answer || fallback
|
|
185
|
+
const error = validate?.(value)
|
|
186
|
+
if (!error) {
|
|
187
|
+
return value
|
|
188
|
+
}
|
|
189
|
+
console.log(` ${c.yellow}${error}${c.reset}`)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function confirm(
|
|
194
|
+
label: string,
|
|
195
|
+
override?: boolean,
|
|
196
|
+
defaultYes = true,
|
|
197
|
+
): Promise<boolean> {
|
|
198
|
+
if (override !== undefined) {
|
|
199
|
+
return override
|
|
200
|
+
}
|
|
201
|
+
while (true) {
|
|
202
|
+
const hint = defaultYes
|
|
203
|
+
? `${c.dim}(Y/n)${c.reset}`
|
|
204
|
+
: `${c.dim}(y/N)${c.reset}`
|
|
205
|
+
const answer = (await question(` ${c.cyan}${label}${c.reset} ${hint} `))
|
|
206
|
+
.trim()
|
|
207
|
+
.toLowerCase()
|
|
208
|
+
if (!answer) {
|
|
209
|
+
return defaultYes
|
|
210
|
+
}
|
|
211
|
+
if (answer === "y" || answer === "yes") {
|
|
212
|
+
return true
|
|
213
|
+
}
|
|
214
|
+
if (answer === "n" || answer === "no") {
|
|
215
|
+
return false
|
|
216
|
+
}
|
|
217
|
+
console.log(` ${c.yellow}Please enter y or n.${c.reset}`)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const step = (msg: string) => console.log(`\n ${c.green}✓${c.reset} ${msg}`)
|
|
222
|
+
|
|
223
|
+
function formatTemplateBadges(template: TemplateMetadata): string {
|
|
224
|
+
return template.recommended ? "recommended" : ""
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function formatTemplateLabel(template: TemplateMetadata): string {
|
|
228
|
+
const badges = formatTemplateBadges(template)
|
|
229
|
+
return `${template.title}${badges ? ` · ${badges}` : ""}`
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function printTemplateCatalog() {
|
|
233
|
+
console.log(` ${c.bold}Available templates${c.reset}`)
|
|
234
|
+
for (const template of templates) {
|
|
235
|
+
console.log(
|
|
236
|
+
` ${c.cyan}${template.name}${c.reset} ${c.dim}(${formatTemplateLabel(template)})${c.reset}`,
|
|
237
|
+
)
|
|
238
|
+
console.log(` ${template.description}`)
|
|
239
|
+
}
|
|
240
|
+
console.log("")
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function printTemplateError(templateName: string) {
|
|
244
|
+
console.error(`\n Template "${templateName}" not found.`)
|
|
245
|
+
console.error(` Available templates:\n`)
|
|
246
|
+
for (const template of templates) {
|
|
247
|
+
console.error(` - ${template.name} (${formatTemplateLabel(template)})`)
|
|
248
|
+
console.error(` ${template.description}`)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function formatShellPath(path: string): string {
|
|
253
|
+
if (!/[\s"'`$\\]/.test(path)) {
|
|
254
|
+
return path
|
|
255
|
+
}
|
|
256
|
+
return `"${path.replace(/["\\$`]/g, "\\$&")}"`
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function formatTemplatePickerLines(
|
|
260
|
+
index: number,
|
|
261
|
+
template: TemplateMetadata,
|
|
262
|
+
selected: boolean,
|
|
263
|
+
): string[] {
|
|
264
|
+
const cursor = selected ? `${c.green}>${c.reset}` : " "
|
|
265
|
+
const number = `${index + 1}.`
|
|
266
|
+
const name = selected ? `${c.bold}${template.name}${c.reset}` : template.name
|
|
267
|
+
const badge = template.recommended ? ` ${c.dim}[recommended]${c.reset}` : ""
|
|
268
|
+
const columns = process.stdout.columns ?? 80
|
|
269
|
+
const descriptionPrefix = " "
|
|
270
|
+
const descriptionWidth = Math.max(20, columns - descriptionPrefix.length - 2)
|
|
271
|
+
const wrappedDescription = wrapText(template.description, descriptionWidth)
|
|
272
|
+
|
|
273
|
+
return [
|
|
274
|
+
` ${cursor} ${number} ${name}${badge}`,
|
|
275
|
+
...wrappedDescription.map(
|
|
276
|
+
line => `${descriptionPrefix}${c.dim}${line}${c.reset}`,
|
|
277
|
+
),
|
|
278
|
+
]
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async function selectTemplateInteractively(
|
|
282
|
+
templateList: TemplateMetadata[],
|
|
283
|
+
defaultTemplate: TemplateMetadata,
|
|
284
|
+
): Promise<TemplateMetadata> {
|
|
285
|
+
closePromptInterface()
|
|
286
|
+
|
|
287
|
+
const defaultIndex = Math.max(
|
|
288
|
+
0,
|
|
289
|
+
templateList.findIndex(template => template.name === defaultTemplate.name),
|
|
290
|
+
)
|
|
291
|
+
const maxDirectJump = Math.min(templateList.length, 9)
|
|
292
|
+
|
|
293
|
+
return new Promise(resolve => {
|
|
294
|
+
let selectedIndex = defaultIndex
|
|
295
|
+
let renderedLineCount = 0
|
|
296
|
+
|
|
297
|
+
const render = () => {
|
|
298
|
+
const lines = [
|
|
299
|
+
` ${c.bold}Choose a template${c.reset}`,
|
|
300
|
+
` ${c.dim}Use up/down (or j/k), Enter to select${maxDirectJump > 0 ? `, 1-${maxDirectJump} to jump` : ""}.${c.reset}`,
|
|
301
|
+
"",
|
|
302
|
+
...templateList.flatMap((template, index) =>
|
|
303
|
+
formatTemplatePickerLines(index, template, index === selectedIndex),
|
|
304
|
+
),
|
|
305
|
+
"",
|
|
306
|
+
]
|
|
307
|
+
|
|
308
|
+
if (renderedLineCount > 0) {
|
|
309
|
+
moveCursor(process.stdout, 0, -renderedLineCount)
|
|
310
|
+
cursorTo(process.stdout, 0)
|
|
311
|
+
clearScreenDown(process.stdout)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
process.stdout.write(lines.join("\n") + "\n")
|
|
315
|
+
renderedLineCount = lines.length
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const cleanup = () => {
|
|
319
|
+
process.stdout.write(`${c.reset}\x1b[?25h`)
|
|
320
|
+
if (renderedLineCount > 0) {
|
|
321
|
+
moveCursor(process.stdout, 0, -renderedLineCount)
|
|
322
|
+
cursorTo(process.stdout, 0)
|
|
323
|
+
clearScreenDown(process.stdout)
|
|
324
|
+
}
|
|
325
|
+
process.stdin.off("keypress", handleKeypress)
|
|
326
|
+
if (process.stdin.isTTY) {
|
|
327
|
+
process.stdin.setRawMode(false)
|
|
328
|
+
}
|
|
329
|
+
process.stdin.pause()
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const handleKeypress = (
|
|
333
|
+
input: string,
|
|
334
|
+
key: { ctrl?: boolean; name?: string },
|
|
335
|
+
) => {
|
|
336
|
+
if (key.ctrl && key.name === "c") {
|
|
337
|
+
cleanup()
|
|
338
|
+
process.stdout.write("\n")
|
|
339
|
+
process.exit(1)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (key.name === "up" || input === "k") {
|
|
343
|
+
selectedIndex =
|
|
344
|
+
(selectedIndex - 1 + templateList.length) % templateList.length
|
|
345
|
+
render()
|
|
346
|
+
return
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (key.name === "down" || input === "j") {
|
|
350
|
+
selectedIndex = (selectedIndex + 1) % templateList.length
|
|
351
|
+
render()
|
|
352
|
+
return
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (input && /^[1-9]$/.test(input)) {
|
|
356
|
+
const jumpIndex = Number(input) - 1
|
|
357
|
+
if (jumpIndex < templateList.length) {
|
|
358
|
+
selectedIndex = jumpIndex
|
|
359
|
+
render()
|
|
360
|
+
}
|
|
361
|
+
return
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (key.name === "return") {
|
|
365
|
+
const selectedTemplate = templateList[selectedIndex]
|
|
366
|
+
cleanup()
|
|
367
|
+
resolve(selectedTemplate)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
emitKeypressEvents(process.stdin)
|
|
372
|
+
process.stdout.write("\x1b[?25l")
|
|
373
|
+
if (process.stdin.isTTY) {
|
|
374
|
+
process.stdin.setRawMode(true)
|
|
375
|
+
}
|
|
376
|
+
process.stdin.resume()
|
|
377
|
+
process.stdin.on("keypress", handleKeypress)
|
|
378
|
+
render()
|
|
379
|
+
})
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async function selectTemplate(
|
|
383
|
+
defaultTemplate: TemplateMetadata,
|
|
384
|
+
override?: string,
|
|
385
|
+
): Promise<TemplateMetadata> {
|
|
386
|
+
if (override !== undefined) {
|
|
387
|
+
const selectedTemplate = getTemplateByName(templateBaseDir, override)
|
|
388
|
+
if (!selectedTemplate) {
|
|
389
|
+
printTemplateError(override)
|
|
390
|
+
process.exit(1)
|
|
391
|
+
}
|
|
392
|
+
return selectedTemplate
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
396
|
+
printTemplateCatalog()
|
|
397
|
+
const templateName = await ask(
|
|
398
|
+
"Template:",
|
|
399
|
+
defaultTemplate.name,
|
|
400
|
+
undefined,
|
|
401
|
+
value =>
|
|
402
|
+
getTemplateByName(templateBaseDir, value)
|
|
403
|
+
? undefined
|
|
404
|
+
: `Choose one of: ${templates.map(template => template.name).join(", ")}`,
|
|
405
|
+
)
|
|
406
|
+
return getTemplateByName(templateBaseDir, templateName) ?? defaultTemplate
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return selectTemplateInteractively(templates, defaultTemplate)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function printSummary(args: {
|
|
413
|
+
dest: string
|
|
414
|
+
name: string
|
|
415
|
+
template: TemplateMetadata
|
|
416
|
+
initGit: boolean
|
|
417
|
+
installDeps: boolean
|
|
418
|
+
}) {
|
|
419
|
+
const { dest, name, template, initGit, installDeps } = args
|
|
420
|
+
console.log(`\n ${c.bold}Summary${c.reset}`)
|
|
421
|
+
console.log(` ${c.dim}Location:${c.reset} ${dest}`)
|
|
422
|
+
console.log(` ${c.dim}Project name:${c.reset} ${name}`)
|
|
423
|
+
console.log(
|
|
424
|
+
` ${c.dim}Template:${c.reset} ${template.name} ${c.dim}(${template.title})${c.reset}`,
|
|
425
|
+
)
|
|
426
|
+
console.log(` ${c.dim}Git:${c.reset} ${initGit ? "yes" : "no"}`)
|
|
427
|
+
console.log(` ${c.dim}Install:${c.reset} ${installDeps ? "yes" : "no"}`)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
async function main() {
|
|
431
|
+
const { args, positional } = parseArgs(process.argv)
|
|
432
|
+
if (templates.length === 0) {
|
|
433
|
+
throw new Error("No templates found in templates/")
|
|
434
|
+
}
|
|
435
|
+
const defaultTemplate =
|
|
436
|
+
templates.find(template => template.recommended) ?? templates[0]
|
|
437
|
+
|
|
438
|
+
console.log(`\n${c.bold} Create a new custom view${c.reset}\n`)
|
|
439
|
+
|
|
440
|
+
const selectedTemplate = await selectTemplate(
|
|
441
|
+
defaultTemplate,
|
|
442
|
+
args.template as string | undefined,
|
|
443
|
+
)
|
|
444
|
+
const name = await ask(
|
|
445
|
+
"Project name:",
|
|
446
|
+
selectedTemplate.name,
|
|
447
|
+
args.name as string | undefined,
|
|
448
|
+
validateProjectName,
|
|
449
|
+
)
|
|
450
|
+
const dir = await ask(
|
|
451
|
+
"Location:",
|
|
452
|
+
positional[0] || `./${name}`,
|
|
453
|
+
positional[0],
|
|
454
|
+
validateLocation,
|
|
455
|
+
)
|
|
456
|
+
const dest = resolve(dir)
|
|
457
|
+
const templateDir = resolve(templateBaseDir, selectedTemplate.name)
|
|
458
|
+
|
|
459
|
+
step(
|
|
460
|
+
`Using template ${c.bold}${selectedTemplate.name}${c.reset} ${c.dim}(${selectedTemplate.title})${c.reset}`,
|
|
461
|
+
)
|
|
462
|
+
console.log(` ${selectedTemplate.description}`)
|
|
463
|
+
|
|
464
|
+
const initGit = await confirm(
|
|
465
|
+
"Initialize git repo?",
|
|
466
|
+
args.git as boolean | undefined,
|
|
467
|
+
)
|
|
468
|
+
const installDeps = await confirm(
|
|
469
|
+
"Install dependencies?",
|
|
470
|
+
args.install as boolean | undefined,
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
printSummary({
|
|
474
|
+
dest,
|
|
475
|
+
name,
|
|
476
|
+
template: selectedTemplate,
|
|
477
|
+
initGit,
|
|
478
|
+
installDeps,
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
scaffoldTemplate({ root, templateDir, dest, name })
|
|
482
|
+
|
|
483
|
+
step(`Scaffolded project in ${c.bold}${dest}${c.reset}`)
|
|
484
|
+
|
|
485
|
+
if (initGit) {
|
|
486
|
+
execSync("git init", { cwd: dest, stdio: "ignore" })
|
|
487
|
+
execSync("git add -A", { cwd: dest, stdio: "ignore" })
|
|
488
|
+
execSync('git commit -m "Initial commit"', { cwd: dest, stdio: "ignore" })
|
|
489
|
+
step("Initialized git repo")
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
if (installDeps) {
|
|
493
|
+
console.log("")
|
|
494
|
+
const installArgs = pm === "pnpm" ? "install --ignore-workspace" : "install"
|
|
495
|
+
execSync(`${pm} ${installArgs}`, { cwd: dest, stdio: "inherit" })
|
|
496
|
+
step("Installed dependencies")
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const blockId = process.env.BLOCK_ID
|
|
500
|
+
const envName = process.env.ENV
|
|
501
|
+
const deployCommand =
|
|
502
|
+
blockId && envName
|
|
503
|
+
? `NOTION_KEYRING=0 ntn --env ${envName} custom deploy --block ${blockId} dist/`
|
|
504
|
+
: blockId
|
|
505
|
+
? `ntn custom deploy --block ${blockId} dist/`
|
|
506
|
+
: undefined
|
|
507
|
+
|
|
508
|
+
console.log(`\n${c.bold} Ready!${c.reset} Next steps:\n`)
|
|
509
|
+
if (dest !== resolve(".")) {
|
|
510
|
+
console.log(` cd ${formatShellPath(dest)}`)
|
|
511
|
+
}
|
|
512
|
+
if (!installDeps) {
|
|
513
|
+
console.log(` ${pm} install`)
|
|
514
|
+
}
|
|
515
|
+
console.log(` ${pm} run build`)
|
|
516
|
+
if (deployCommand) {
|
|
517
|
+
console.log(` ${deployCommand}\n`)
|
|
518
|
+
} else {
|
|
519
|
+
console.log(
|
|
520
|
+
`\n Then, go back to the instructions in Notion and paste the ${c.bold}ntn deploy${c.reset} command.\n`,
|
|
521
|
+
)
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
closePromptInterface()
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
main()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# {{name}}
|
|
2
|
+
|
|
3
|
+
A **Notion custom view** — a sandboxed `<iframe>` rendered inside a Notion block. The iframe is the entire viewport; the only channel to the host is a `postMessage` bridge wrapped by `ncblock`.
|
|
4
|
+
|
|
5
|
+
## Talking to the host
|
|
6
|
+
|
|
7
|
+
- Always use the React hooks from `ncblock`. Never call `window.parent.postMessage` directly — the SDK owns the protocol.
|
|
8
|
+
- Render app code inside `<NotionCustomBlock>` so the handshake completes before hooks like `useTheme` or `useCustomBlockContext` run.
|
|
9
|
+
|
|
10
|
+
Hooks at a glance:
|
|
11
|
+
|
|
12
|
+
- `useCustomBlockContext()` — `{ customBlockId, parent, page }`.
|
|
13
|
+
- `useTheme()` — `"light" | "dark"`.
|
|
14
|
+
- `useDataSource(key, initialLimit?)` — `{ items, isLoading, hasMore, fetchMore, error }`.
|
|
15
|
+
- `useDataSourceDefinitions()` — resolved data-source definitions (for debug/schema-driven UIs).
|
|
16
|
+
- `pages.create(input)` — creates a page; pass `parent: { type: "data_source_key", key }` to target the block's wired data source.
|
|
17
|
+
|
|
18
|
+
`<NotionCustomBlock>` runs `useCustomBlockAutoResize` for you by default — no extra wiring needed. Pass `autoResize={false}` for full-bleed views. Full signatures live in `node_modules/ncblock/docs/*.md` and the `.d.ts` files.
|
|
19
|
+
|
|
20
|
+
## Sizing
|
|
21
|
+
|
|
22
|
+
- The host owns width and height — no hard-coded widths. Layouts must reflow from a phone column to a desktop block.
|
|
23
|
+
- `100vh` ≠ a screen inside an iframe. Use intrinsic sizing with `min-height`.
|
|
24
|
+
- Use container queries (`@container`) — iframe width, not device width, is the real constraint.
|
|
25
|
+
|
|
26
|
+
## Forbidden APIs
|
|
27
|
+
|
|
28
|
+
- No top-level navigation, `window.open`, or auth redirects.
|
|
29
|
+
- No network requests. All data comes through host data sources.
|
|
30
|
+
|
|
31
|
+
## Conventions
|
|
32
|
+
|
|
33
|
+
- Mount into `<div id="root">` — `useCustomBlockAutoResize` looks for that exact id.
|
|
34
|
+
- `custom_blocks.json` at the project root defines the data contract. The SDK's Vite plugin serves it in dev; `ntn` bundles it into the deploy.
|
|
35
|
+
|
|
36
|
+
## Previewing during development
|
|
37
|
+
|
|
38
|
+
Two options:
|
|
39
|
+
|
|
40
|
+
1. **Live Notion host** — ask the user to create a block via `/custom` and paste the Vite dev URL (e.g. `http://localhost:5173`) into the block's URL field.
|
|
41
|
+
2. **Mock host** — run the dev shell in another tab:
|
|
42
|
+
```bash
|
|
43
|
+
git clone https://github.com/makenotion/custom
|
|
44
|
+
cd custom && pnpm install && pnpm run dev:shell # http://localhost:9875
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Deployment
|
|
48
|
+
|
|
49
|
+
Ask the user to create a block via `/custom` and copy the deploy command. It looks like:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
ntn deploy --block <block-id> <dist-path>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Where to look next
|
|
56
|
+
|
|
57
|
+
- `node_modules/ncblock/README.md` — landing page with a TOC into the per-category docs below.
|
|
58
|
+
- `node_modules/ncblock/docs/lifecycle.md` — `<NotionCustomBlock>`, init, sizing, auto-resize.
|
|
59
|
+
- `node_modules/ncblock/docs/context.md` — `useCustomBlockContext`, `useTheme`.
|
|
60
|
+
- `node_modules/ncblock/docs/data-sources.md` — `useDataSource`, row/property/date types, worked example.
|
|
61
|
+
- `node_modules/ncblock/docs/pages.md` — `pages.create / get / update / delete`.
|
|
62
|
+
- `node_modules/ncblock/docs/users.md` — `users.list / get`, `NotionUser`.
|
|
63
|
+
- `node_modules/ncblock/docs/manifest.md` — `custom_blocks.json` + Vite plugin.
|
|
64
|
+
- `node_modules/ncblock/dist/*.d.ts` — typed surface; hover in your editor.
|
|
65
|
+
- https://github.com/makenotion/custom — source and examples.
|