create-bcp-app 0.1.3 → 0.1.5

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 CHANGED
@@ -20,6 +20,20 @@ Select a database:
20
20
  5) MongoDB
21
21
  ```
22
22
 
23
+ ## Generated application defaults
24
+
25
+ Generated applications include the project-root `@/` alias:
26
+
27
+ ```ts
28
+ import {
29
+ db,
30
+ } from "@/lib/database";
31
+ ```
32
+
33
+ The generated TypeScript configuration uses `module: "ESNext"` and `moduleResolution: "Bundler"` so extensionless application aliases match BCP's bundler and development runtime resolution.
34
+
35
+ Modules that use React client hooks such as `useState` or `useEffect` must declare `"use client"` at the top of the module.
36
+
23
37
  ## Generated optional setup
24
38
 
25
39
  ### Tailwind CSS
@@ -27,9 +41,21 @@ Select a database:
27
41
  When Tailwind is enabled, the project includes:
28
42
 
29
43
  - `tailwindcss`
30
- - `@tailwindcss/postcss`
31
- - `postcss.config.mjs`
44
+ - `@tailwindcss/cli`
45
+ - `concurrently`
32
46
  - `app/globals.css`
47
+ - a stylesheet link to `/bcp.css` in `app/layout.tsx`
48
+ - starter Tailwind utility classes in `app/page.tsx`
49
+
50
+ The generated npm scripts compile Tailwind directly to `public/bcp.css` before starting BCP and keep Tailwind running in watch mode during development:
51
+
52
+ ```text
53
+ npm run css:build
54
+ npm run css:watch
55
+ npm run css:build:prod
56
+ ```
57
+
58
+ `npm run dev` performs an initial CSS build, then runs the Tailwind watcher and `bcp dev` together. BCP serves `/bcp.css` as a normal static asset from `public/`, avoiding an extra API route in the critical request chain. `npm run build` creates a minified stylesheet before the BCP production build.
33
59
 
34
60
  ### Database
35
61
 
@@ -41,6 +67,14 @@ The database choice adds a starter `lib/database.ts`, updates `.env.example`, an
41
67
  - MongoDB: `mongodb`
42
68
  - None: no database dependency
43
69
 
70
+ The generated database helper starts with:
71
+
72
+ ```ts
73
+ import "bcp/server-only";
74
+ ```
75
+
76
+ This prevents database code from being used in a page/client module graph. Query the database from an API route and call that API from client components.
77
+
44
78
  The generator intentionally installs database drivers directly and does not force an ORM.
45
79
 
46
80
  ## Options
@@ -66,5 +100,5 @@ npx create-bcp-app my-app --yes
66
100
  The `--bcp` option is primarily useful for prerelease and local package testing, for example:
67
101
 
68
102
  ```bash
69
- npx create-bcp-app my-app --bcp file:../bcp-0.1.2.tgz
103
+ npx create-bcp-app my-app --bcp file:../bcp-0.1.5.tgz
70
104
  ```
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "create-bcp-app",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Create a new BCP Framework application.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
+ "bcpFrameworkPackage": "@chidchanun/bcp",
7
8
  "bin": {
8
9
  "create-bcp-app": "bin/create-bcp-app.mjs"
9
10
  },
@@ -24,6 +25,5 @@
24
25
  },
25
26
  "publishConfig": {
26
27
  "access": "public"
27
- },
28
- "bcpFrameworkPackage": "@chidchanun/bcp"
28
+ }
29
29
  }
@@ -18,7 +18,9 @@ const DATABASE_PRESETS = {
18
18
  env: [
19
19
  "DATABASE_URL=mysql://root:password@localhost:3306/bcp_app",
20
20
  ],
21
- source: `import mysql from "mysql2/promise";
21
+ source: `import "bcp/server-only";
22
+
23
+ import mysql from "mysql2/promise";
22
24
 
23
25
  const connectionString =
24
26
  process.env.DATABASE_URL;
@@ -45,7 +47,9 @@ export const db =
45
47
  env: [
46
48
  "DATABASE_URL=postgresql://postgres:password@localhost:5432/bcp_app",
47
49
  ],
48
- source: `import {
50
+ source: `import "bcp/server-only";
51
+
52
+ import {
49
53
  Pool,
50
54
  } from "pg";
51
55
 
@@ -74,7 +78,9 @@ export const db =
74
78
  env: [
75
79
  "DATABASE_URL=./data/bcp.sqlite",
76
80
  ],
77
- source: `import fs from "node:fs";
81
+ source: `import "bcp/server-only";
82
+
83
+ import fs from "node:fs";
78
84
  import path from "node:path";
79
85
 
80
86
  import Database from "better-sqlite3";
@@ -111,7 +117,9 @@ export const db =
111
117
  "DATABASE_URL=mongodb://localhost:27017",
112
118
  "DATABASE_NAME=bcp_app",
113
119
  ],
114
- source: `import {
120
+ source: `import "bcp/server-only";
121
+
122
+ import {
115
123
  MongoClient,
116
124
  } from "mongodb";
117
125
 
@@ -176,34 +184,9 @@ export function applyProjectOptions({
176
184
  );
177
185
 
178
186
  if (tailwind) {
179
- packageJson.devDependencies = {
180
- ...packageJson.devDependencies,
181
- tailwindcss:
182
- "^4.3.3",
183
- "@tailwindcss/postcss":
184
- "^4.3.3",
185
- };
186
-
187
- writeFile(
187
+ configureTailwind(
188
188
  targetDirectory,
189
- "postcss.config.mjs",
190
- `export default {
191
- plugins: {
192
- "@tailwindcss/postcss": {},
193
- },
194
- };
195
- `
196
- );
197
-
198
- writeFile(
199
- targetDirectory,
200
- "app/globals.css",
201
- `@import "tailwindcss";
202
-
203
- @theme {
204
- --font-sans: Inter, ui-sans-serif, system-ui, sans-serif;
205
- }
206
- `
189
+ packageJson
207
190
  );
208
191
  }
209
192
 
@@ -245,6 +228,191 @@ export function applyProjectOptions({
245
228
  };
246
229
  }
247
230
 
231
+ function configureTailwind(
232
+ targetDirectory,
233
+ packageJson
234
+ ) {
235
+ packageJson.devDependencies = {
236
+ ...packageJson.devDependencies,
237
+ tailwindcss:
238
+ "^4.3.3",
239
+ "@tailwindcss/cli":
240
+ "^4.3.3",
241
+ concurrently:
242
+ "^10.0.5",
243
+ };
244
+
245
+ packageJson.scripts = {
246
+ ...packageJson.scripts,
247
+ dev:
248
+ "npm run css:build && concurrently -k -n tailwind,bcp \"npm run css:watch\" \"bcp dev\"",
249
+ build:
250
+ "npm run css:build:prod && bcp build",
251
+ "css:build":
252
+ "tailwindcss -i ./app/globals.css -o ./public/bcp.css",
253
+ "css:watch":
254
+ "tailwindcss -i ./app/globals.css -o ./public/bcp.css --watch",
255
+ "css:build:prod":
256
+ "tailwindcss -i ./app/globals.css -o ./public/bcp.css --minify",
257
+ };
258
+
259
+ writeFile(
260
+ targetDirectory,
261
+ "app/globals.css",
262
+ `@import "tailwindcss" source("../");
263
+
264
+ @theme {
265
+ --font-sans: Inter, ui-sans-serif, system-ui, sans-serif;
266
+ }
267
+ `
268
+ );
269
+
270
+ fs.mkdirSync(
271
+ path.join(
272
+ targetDirectory,
273
+ "public"
274
+ ),
275
+ {
276
+ recursive: true,
277
+ }
278
+ );
279
+
280
+ injectTailwindStylesheet(
281
+ targetDirectory
282
+ );
283
+ applyTailwindStarterStyles(
284
+ targetDirectory
285
+ );
286
+ appendGitIgnore(
287
+ targetDirectory,
288
+ "public/bcp.css"
289
+ );
290
+ }
291
+
292
+ function injectTailwindStylesheet(
293
+ targetDirectory
294
+ ) {
295
+ const layoutFile =
296
+ path.join(
297
+ targetDirectory,
298
+ "app",
299
+ "layout.tsx"
300
+ );
301
+ const source =
302
+ fs.readFileSync(
303
+ layoutFile,
304
+ "utf8"
305
+ );
306
+ const rootPattern =
307
+ /^([ \t]*)<div>[ \t]*(\r?\n)/m;
308
+ const match =
309
+ rootPattern.exec(
310
+ source
311
+ );
312
+
313
+ if (!match) {
314
+ throw new Error(
315
+ "BCP create-app could not attach the Tailwind stylesheet to app/layout.tsx."
316
+ );
317
+ }
318
+
319
+ const indent =
320
+ match[1];
321
+ const newline =
322
+ match[2];
323
+ const childIndent =
324
+ `${indent} `;
325
+ const propertyIndent =
326
+ `${childIndent} `;
327
+ const replacement =
328
+ `${indent}<div>${newline}` +
329
+ `${childIndent}<link${newline}` +
330
+ `${propertyIndent}rel="stylesheet"${newline}` +
331
+ `${propertyIndent}href="/bcp.css"${newline}` +
332
+ `${childIndent}/>${newline}`;
333
+
334
+ fs.writeFileSync(
335
+ layoutFile,
336
+ source.replace(
337
+ rootPattern,
338
+ replacement
339
+ ),
340
+ "utf8"
341
+ );
342
+ }
343
+
344
+ function applyTailwindStarterStyles(
345
+ targetDirectory
346
+ ) {
347
+ const pageFile =
348
+ path.join(
349
+ targetDirectory,
350
+ "app",
351
+ "page.tsx"
352
+ );
353
+ let source =
354
+ fs.readFileSync(
355
+ pageFile,
356
+ "utf8"
357
+ );
358
+
359
+ source =
360
+ source.replace(
361
+ " <section>",
362
+ " <section className=\"mx-auto max-w-3xl space-y-6 px-6 py-16\">"
363
+ );
364
+ source =
365
+ source.replace(
366
+ " <h1>",
367
+ " <h1 className=\"text-4xl font-bold tracking-tight text-slate-900\">"
368
+ );
369
+ source =
370
+ source.replace(
371
+ " <p>",
372
+ " <p className=\"text-lg text-slate-600\">"
373
+ );
374
+
375
+ fs.writeFileSync(
376
+ pageFile,
377
+ source,
378
+ "utf8"
379
+ );
380
+ }
381
+
382
+ function appendGitIgnore(
383
+ targetDirectory,
384
+ entry
385
+ ) {
386
+ const filePath =
387
+ path.join(
388
+ targetDirectory,
389
+ ".gitignore"
390
+ );
391
+ const current =
392
+ fs.existsSync(
393
+ filePath
394
+ )
395
+ ? fs.readFileSync(
396
+ filePath,
397
+ "utf8"
398
+ ).trimEnd()
399
+ : "";
400
+ const lines =
401
+ new Set(
402
+ current
403
+ .split(/\r?\n/)
404
+ .filter(Boolean)
405
+ );
406
+
407
+ lines.add(entry);
408
+
409
+ fs.writeFileSync(
410
+ filePath,
411
+ `${Array.from(lines).join("\n")}\n`,
412
+ "utf8"
413
+ );
414
+ }
415
+
248
416
  function appendEnvExample(
249
417
  targetDirectory,
250
418
  lines
@@ -1,14 +1,20 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
6
  "jsx": "react-jsx",
7
7
  "strict": true,
8
8
  "noEmit": true,
9
9
  "esModuleInterop": true,
10
10
  "allowSyntheticDefaultImports": true,
11
11
  "skipLibCheck": true,
12
+ "baseUrl": ".",
13
+ "paths": {
14
+ "@/*": [
15
+ "./*"
16
+ ]
17
+ },
12
18
  "types": [
13
19
  "node",
14
20
  "react",