create-keboola-app 0.1.0 → 0.1.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/CHANGELOG.md CHANGED
@@ -1,10 +1,29 @@
1
1
  # create-keboola-app
2
2
 
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - A generated app pins `nwsapi`, so a userEvent test does not hang.
8
+
9
+ nwsapi 2.2.26 — jsdom's selector engine — answers `:modal`, `:fullscreen` and `:open` through a helper that falls back to the element's own `matches`. Under jsdom that is nwsapi itself, so the two recurse, and `:modal` doubles the work per level through its `|| isFullscreen` branch. `@floating-ui/dom` probes `:modal` on every popper placement, which is once per dropdown or tooltip interaction: measured at 9s where 2.2.25 takes 26ms, so a test driving a menu with `userEvent` hits its timeout instead of asserting.
10
+
11
+ Generated apps now ship a `pnpm-workspace.yaml` holding `nwsapi` at 2.2.25 — the only file pnpm reads `overrides` from. It carries the reason and the condition for removing it.
12
+
13
+ - Name the generated app in its own chrome. The breadcrumb was hard-coded to the template's own
14
+ name, so every scaffold shipped labelled after the template rather than the name the user passed
15
+ — the first thing on screen, and the one place the app names itself back to the reader. The
16
+ extract step now parameterises it alongside the page title and the store's storage keys.
17
+
18
+ A `boilerplate-identity` sanitize pattern makes this class of leak a red build instead of a
19
+ silent default: any un-templated mention of the source app's name fails extraction, so a rewrite
20
+ that stops matching is caught rather than shipped.
21
+
3
22
  ## 0.1.0
4
23
 
5
24
  ### Minor Changes
6
25
 
7
- - [#8229](https://github.com/keboola/ui/pull/8229) [`67848b3`](https://github.com/keboola/ui/commit/67848b3b89cc39b40f7c47edca838dcc9c0411dc) Thanks [@Mystypix](https://github.com/Mystypix)! - Initial release. `npm create keboola-app` scaffolds a Keboola-shaped frontend app — Vite,
26
+ - Initial release. `npm create keboola-app` scaffolds a Keboola-shaped frontend app — Vite,
8
27
  React 19, TanStack Router/Query/Form, Zustand, `@keboola/design` with
9
28
  `@keboola/brand-registry`, oxlint/oxfmt and vitest — from a minimal shell plus the blocks you
10
29
  pick: `kpi`, `chart`, `grid`, `crud`, `assistant`. Blocks are independent, so any combination
@@ -19,6 +38,6 @@
19
38
 
20
39
  ### Patch Changes
21
40
 
22
- - [#8233](https://github.com/keboola/ui/pull/8233) [`6c449f9`](https://github.com/keboola/ui/commit/6c449f9ed8024551aaaf2a8b3a24ed109f7d263d) Thanks [@Mystypix](https://github.com/Mystypix)! - In `--workspace` mode the generated app inherits the host repo's `engines`, and the monorepo entry point is `pnpm scaffold:app`.
41
+ - In `--workspace` mode the generated app inherits the host repo's `engines`, and the monorepo entry point is `pnpm scaffold:app`.
23
42
 
24
43
  The template carries a standalone `engines.node` floor for apps generated outside a monorepo. Inside one that floor is not a default but a disagreement: it was the lowest value across every workspace, so syncpack wanted to rewrite the other 21 manifests and `pnpm deps:check` went red on a freshly scaffolded app. Workspace mode now reads the root manifest's `engines` instead, and the gate asserts it.
package/dist/cli.js CHANGED
@@ -196,6 +196,29 @@ const writeManifest = (opts) => {
196
196
  };
197
197
  writeFileSync(join(targetDir, "package.json"), `${JSON.stringify(pkg, null, 2)}\n`);
198
198
  };
199
+ /**
200
+ * `pnpm-workspace.yaml` for a standalone app.
201
+ *
202
+ * - It is the only file pnpm 11 reads `overrides` from; the `pnpm` field in `package.json` is
203
+ * ignored with a warning.
204
+ * - Written only outside a monorepo. Under `--workspace` the app inherits the root's file, and a
205
+ * nested one would declare a second workspace root inside the first.
206
+ */
207
+ const writeWorkspaceConfig = (targetDir) => {
208
+ writeFileSync(join(targetDir, "pnpm-workspace.yaml"), `# pnpm reads \`overrides\` from this file only — the \`pnpm\` field in package.json is ignored.
209
+ overrides:
210
+ # jsdom's selector engine, held at the last release that does not re-enter itself.
211
+ #
212
+ # - 2.2.26 answers \`:modal\`, \`:fullscreen\` and \`:open\` through a \`matchesNative\` helper
213
+ # that falls back to the element's own \`matches\` — under jsdom that is nwsapi again, so the
214
+ # two recurse, and \`:modal\` doubles the work per level via its \`|| isFullscreen\` branch.
215
+ # - \`@floating-ui/dom\` probes \`:modal\` on every popper placement, so one dropdown or tooltip
216
+ # interaction costs ~9s instead of ~30ms and a userEvent test hits its timeout.
217
+ # - Remove the pin once nwsapi stops resolving its own \`matches\` back into itself:
218
+ # https://github.com/dperini/nwsapi
219
+ nwsapi: 2.2.25
220
+ `);
221
+ };
199
222
  const scaffold = (opts) => {
200
223
  const { templateDir, targetDir, appName, blocks, workspace } = opts;
201
224
  if (existsSync(targetDir)) throw new Error(`${targetDir} already exists. Choose another name or remove the directory first.`);
@@ -231,6 +254,10 @@ const scaffold = (opts) => {
231
254
  packageName
232
255
  });
233
256
  written.push("package.json");
257
+ if (!workspace) {
258
+ writeWorkspaceConfig(targetDir);
259
+ written.push("pnpm-workspace.yaml");
260
+ }
234
261
  return {
235
262
  path: targetDir,
236
263
  appName,
package/dist/extract.js CHANGED
@@ -47,9 +47,19 @@ const decideRegion = (regionId) => {
47
47
  if (id === null || !isBlockId(id)) throw new Error(`unknown region id "${regionId}" — expected "internal" or "block:<blockId>"`);
48
48
  return "keep-with-markers";
49
49
  };
50
- /** Per-file rewrites that turn boilerplate identity into placeholders. */
50
+ /**
51
+ * Per-file rewrites that turn this app's identity into placeholders.
52
+ *
53
+ * - Every rewrite has a matching assertion in `extract.test.ts`, and the sanitize scan fails
54
+ * the build on any surviving mention of this app's name — a rewrite that stops matching is
55
+ * a red build, not a scaffold that quietly names itself after the template.
56
+ * - Match structurally, not by the literal name. The name must not appear here: the packed
57
+ * tarball is scanned too, and a rewrite that spells its own needle makes the scanner match
58
+ * its own implementation.
59
+ */
51
60
  const parameterise = (file, text) => {
52
61
  if (file === "index.html") return text.replace(/<title>[^<]*<\/title>/, "<title>__APP_TITLE__</title>");
62
+ if (file === "src/components/AppChrome.tsx") return text.replace(/(<Link to="\/">)[^<]*(<\/Link>)/, "$1__APP_TITLE__$2");
53
63
  if (file === "src/store.ts") return text.replaceAll(/'boilerplate\/([^']+)'/g, "'__APP_NAME__/$1'");
54
64
  return text;
55
65
  };
@@ -19,10 +19,8 @@ export default defineConfig({
19
19
  },
20
20
  },
21
21
  jsPlugins: [
22
- // Local design-system + brand-safety lint rules — see
23
- // packages/eslint-plugin-design. Aliased to `design` so rule IDs read
24
- // `design/<rule>` in lint output. Boilerplate is the canonical scaffold
25
- // cloned by `pnpm scaffold:app`, so every generated app inherits these.
22
+ // Local design-system + brand-safety lint rules. Aliased to `design` so rule
23
+ // IDs read `design/<rule>` in lint output.
26
24
  { name: 'design', specifier: '@keboola/eslint-plugin-design' },
27
25
  ],
28
26
  rules: {
@@ -15,8 +15,8 @@
15
15
  "type-check": "tsc"
16
16
  },
17
17
  "dependencies": {
18
- "@keboola/brand-registry": "^1.9.2",
19
- "@keboola/design": "^11.0.1",
18
+ "@keboola/brand-registry": "^1.9.4",
19
+ "@keboola/design": "^11.3.0",
20
20
  "@tanstack/react-form": "^1.29.1",
21
21
  "@tanstack/react-query": "^5.96.2",
22
22
  "@tanstack/react-router": "^1.120.4",
@@ -28,9 +28,9 @@
28
28
  "zustand": "^5.0.8"
29
29
  },
30
30
  "devDependencies": {
31
- "@keboola/eslint-plugin-design": "^0.4.2",
32
- "@keboola/oxlint-config": "^10.0.1",
33
- "@keboola/tsconfig": "^0.2.0",
31
+ "@keboola/eslint-plugin-design": "^0.4.3",
32
+ "@keboola/oxlint-config": "^10.0.2",
33
+ "@keboola/tsconfig": "^0.2.1",
34
34
  "@tailwindcss/vite": "^4.3.3",
35
35
  "@tanstack/react-query-devtools": "^5.96.2",
36
36
  "@testing-library/jest-dom": "^6.8.0",
@@ -89,7 +89,7 @@ export function AppChrome({ children }: { children: ReactNode }) {
89
89
  <Navbar.Menu marker="forward-slash">
90
90
  <Navbar.MenuItem>
91
91
  <Navbar.MenuButton asChild>
92
- <Link to="/">Boilerplate</Link>
92
+ <Link to="/">__APP_TITLE__</Link>
93
93
  </Navbar.MenuButton>
94
94
  </Navbar.MenuItem>
95
95
  </Navbar.Menu>
package/package.json CHANGED
@@ -1,10 +1,9 @@
1
1
  {
2
2
  "name": "create-keboola-app",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Scaffold a Keboola-shaped frontend app — Vite + React 19 + TanStack Router/Query/Form + Zustand + @keboola/design — from a minimal shell plus the blocks you pick",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
- "bin": "./dist/cli.js",
8
7
  "files": [
9
8
  "dist",
10
9
  "README.md",
@@ -16,17 +15,6 @@
16
15
  "engines": {
17
16
  "node": ">=20"
18
17
  },
19
- "scripts": {
20
- "build": "tsdown && node dist/extract.js",
21
- "lint": "pnpm lint:format && pnpm lint:oxlint",
22
- "lint:fix": "oxlint --fix . && oxfmt",
23
- "lint:format": "oxfmt --check",
24
- "lint:oxlint": "oxlint . --quiet",
25
- "knip:check": "pnpm --dir ../.. exec knip --workspace packages/create-keboola-app",
26
- "knip:fix": "pnpm --dir ../.. exec knip --workspace packages/create-keboola-app --fix-type types,exports,files",
27
- "test": "vitest run --passWithNoTests --reporter=dot",
28
- "type-check": "tsc"
29
- },
30
18
  "repository": {
31
19
  "type": "git",
32
20
  "url": "https://github.com/keboola/ui.git",
@@ -50,8 +38,6 @@
50
38
  "@tanstack/router-generator": "^1.120.4"
51
39
  },
52
40
  "devDependencies": {
53
- "@keboola/oxlint-config": "workspace:^",
54
- "@keboola/tsconfig": "workspace:^",
55
41
  "@types/node": "24.12.0",
56
42
  "@typescript/native": "npm:typescript@^7.0.2",
57
43
  "oxfmt": "^0.57.0",
@@ -59,6 +45,22 @@
59
45
  "tsdown": "^0.22.14",
60
46
  "typescript": "npm:@typescript/typescript6@^6.0.2",
61
47
  "vite": "^8.1.5",
62
- "vitest": "^4.1.10"
48
+ "vitest": "^4.1.10",
49
+ "@keboola/oxlint-config": "^10.0.2",
50
+ "@keboola/tsconfig": "^0.2.1"
51
+ },
52
+ "scripts": {
53
+ "build": "tsdown && node dist/extract.js",
54
+ "lint": "pnpm lint:format && pnpm lint:oxlint",
55
+ "lint:fix": "oxlint --fix . && oxfmt",
56
+ "lint:format": "oxfmt --check",
57
+ "lint:oxlint": "oxlint . --quiet",
58
+ "knip:check": "pnpm --dir ../.. exec knip --workspace packages/create-keboola-app",
59
+ "knip:fix": "pnpm --dir ../.. exec knip --workspace packages/create-keboola-app --fix-type types,exports,files",
60
+ "test": "vitest run --passWithNoTests --reporter=dot",
61
+ "type-check": "tsc"
62
+ },
63
+ "bin": {
64
+ "create-keboola-app": "./dist/cli.js"
63
65
  }
64
- }
66
+ }