create-du-app 0.1.1 → 0.1.3

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
@@ -1,64 +1,81 @@
1
1
  # create-du-app
2
2
 
3
- > The `create-app` CLI — scaffolds a standalone product monorepo from company templates.
3
+ > Scaffold a standalone product monorepo from company templates — in one command.
4
4
 
5
- Prompts for a project name and a selection of stacks (Mobile / Frontend / Backend), then copies
6
- the matching templates, renames manifests, substitutes the project name, and emits a ready-to-run
7
- pnpm workspace.
5
+ Run it, answer a couple of prompts (project name + which stacks), and you get a ready-to-run
6
+ pnpm monorepo with only the stacks you picked.
8
7
 
9
- ## Installation
8
+ ## Quick start
9
+
10
+ No install needed. In a real terminal (Terminal.app / iTerm):
10
11
 
11
12
  ```bash
12
- cd packages/cli
13
- pnpm install
14
- pnpm link --global # registers the global `create-app` command
13
+ npx create-du-app
15
14
  ```
16
15
 
17
- Or run without linking:
16
+ You'll be asked:
17
+
18
+ 1. **Project name?** → e.g. `my-shop`
19
+ 2. **Select the groups** → press **`Space`** to tick Mobile / Frontend / Backend, then **`Enter`**
20
+ 3. **Pick one technology** per group → **↑ / ↓** then **`Enter`**
21
+
22
+ Then:
18
23
 
19
24
  ```bash
20
- node src/index.js
25
+ cd my-shop && pnpm install
21
26
  ```
22
27
 
23
- ## Usage
28
+ Done. 🎉
24
29
 
25
- **Interactive** (requires a real terminal):
30
+ ## Non-interactive (CI / IDE terminals)
31
+
32
+ Pass everything as flags — no prompts, works anywhere:
26
33
 
27
34
  ```bash
28
- create-app
35
+ npx create-du-app --name my-shop --mobile expo --fe nextjs --be nestjs
29
36
  ```
30
37
 
31
- **Non-interactive** (CI or any terminal):
38
+ | Flag | Values | Required |
39
+ |------|--------|----------|
40
+ | `--name` | your project name | ✅ |
41
+ | `--mobile` | `rn` · `expo` · `flutter` | optional |
42
+ | `--fe` | `nextjs` · `reactjs` | optional |
43
+ | `--be` | `nodejs` · `nestjs` · `php` | optional |
44
+
45
+ ## Install globally (optional)
46
+
47
+ Scaffold often? Install once and use the shorter `create-app` command:
32
48
 
33
49
  ```bash
34
- create-app --name my-shop --mobile expo --fe nextjs --be nestjs
50
+ npm install -g create-du-app # or: pnpm add -g create-du-app
51
+ create-app # same as npx create-du-app
35
52
  ```
36
53
 
37
- ### Options
54
+ > The **package** is `create-du-app`; the installed **command** is `create-app`
55
+ > (same pattern as `create-next-app`).
38
56
 
39
- | Flag | Description |
40
- |----------------------|------------------------------------------|
41
- | `-n`, `--name <name>`| Project name |
42
- | `--mobile <id>` | `rn` · `expo` · `flutter` |
43
- | `--fe <id>` | `nextjs` · `reactjs` |
44
- | `--be <id>` | `nodejs` · `nestjs` · `php` |
45
- | `-h`, `--help` | Print help |
46
- | `-v`, `--version` | Print version |
57
+ ## Available stacks
47
58
 
48
- ## How it works
59
+ | Group | Technologies | Output folder |
60
+ |----------|-------------------------------|----------------|
61
+ | Mobile | React Native · Expo · Flutter | `apps/mobile` |
62
+ | Frontend | Next.js · ReactJS | `apps/web` |
63
+ | Backend | Node.js · NestJS · PHP | `apps/backend` |
49
64
 
50
- | File | Responsibility |
51
- |---------------|----------------|
52
- | `index.js` | Entry point. Parses flags, decides interactive vs. non-interactive, runs the generator. |
53
- | `registry.js` | **Data declaration** of groups, technologies, `templatePath`, and `lang`. The only file to edit when adding or removing a stack. |
54
- | `prompts.js` | Interactive prompts (`@clack/prompts`): project name → multi-select groups → select one technology per group. |
55
- | `generate.js` | Copies templates, renames `_package.json` → `package.json`, generates the root `package.json` + `pnpm-workspace.yaml`, conditionally creates `packages/shared`, and substitutes `{{PROJECT_NAME}}`. |
65
+ `packages/shared` (shared types + api client) is added automatically when you pick **≥ 2** JS/TS stacks.
66
+
67
+ ---
56
68
 
57
- ## Template conventions
69
+ ## For maintainers
58
70
 
59
- - A manifest named `_package.json` is renamed to `package.json` on generate. Likewise
60
- `_gitignore` `.gitignore` and `_npmrc` → `.npmrc`.
61
- - Any text file containing `{{PROJECT_NAME}}` has it replaced with the real project name.
62
- - `packages/shared` is created only when **≥ 2** selections have `lang === 'js'`.
71
+ Working on the CLI source or the templates? See the
72
+ [repository README](../../README.md) and **[CONTRIBUTING.md](../../CONTRIBUTING.md)**.
73
+
74
+ | File | Responsibility |
75
+ |---------------|----------------|
76
+ | `src/index.js` | Entry point. Parses flags, runs interactive or non-interactive, then generates. |
77
+ | `src/registry.js` | **Data declaration** of groups, technologies, `templatePath`, and `lang`. Edit this to add/remove a stack. |
78
+ | `src/prompts.js` | Interactive prompts (`@clack/prompts`). |
79
+ | `src/generate.js` | Copies templates, renames `_package.json` → `package.json`, emits the root `package.json` + `pnpm-workspace.yaml`, substitutes `{{PROJECT_NAME}}`. |
63
80
 
64
- See **[CONTRIBUTING.md](../../CONTRIBUTING.md)** for the full workflow of adding real source.
81
+ Release a new version: `pnpm release` (see [scripts/release.mjs](scripts/release.mjs)).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-du-app",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "CLI generator: pick templates (Mobile/FE/BE) and scaffold a product monorepo",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,6 +12,7 @@
12
12
  ],
13
13
  "scripts": {
14
14
  "start": "node src/index.js",
15
+ "release": "node scripts/release.mjs",
15
16
  "prepack": "node scripts/bundle-templates.mjs",
16
17
  "postpack": "node scripts/bundle-templates.mjs --clean"
17
18
  },
package/src/generate.js CHANGED
@@ -1,13 +1,24 @@
1
- // generate.js — copy templates + rename + generate dynamic package.json + replace placeholders.
1
+ // generate.js — copy templates + rename + generate dynamic root config + replace placeholders.
2
2
  //
3
3
  // Input: the plan from prompts.js → { projectName, selections: [{group, option}] }
4
4
  // and `repoRoot` (the company-starter root that contains templates/).
5
+ //
6
+ // The output is a standard pnpm + Turborepo monorepo (modeled on the life-master layout):
7
+ // <project>/
8
+ // package.json (turbo scripts + devDeps)
9
+ // pnpm-workspace.yaml
10
+ // turbo.json
11
+ // tsconfig.base.json (paths: @repo/* -> packages/*/src)
12
+ // .gitignore .npmrc
13
+ // apps/<web|backend|mobile>/ (selected stacks — source dropped in later)
14
+ // packages/shared/ (@repo/shared — only when >= 2 JS/TS apps)
5
15
 
6
16
  import path from 'node:path';
7
17
  import fse from 'fs-extra';
8
18
  import {
9
19
  SHARED_TEMPLATE_PATH,
10
20
  SHARED_OUTPUT_DIR,
21
+ SHARED_PACKAGE_NAME,
11
22
  } from './registry.js';
12
23
 
13
24
  const PLACEHOLDER = /\{\{PROJECT_NAME\}\}/g;
@@ -38,7 +49,6 @@ function renameBasename(name) {
38
49
 
39
50
  // Copy one template folder → destination, renaming files and replacing {{PROJECT_NAME}}.
40
51
  async function copyTemplate(srcDir, destDir, projectName) {
41
- // An empty template (real source not dropped in yet) must still work.
42
52
  if (!(await fse.pathExists(srcDir))) {
43
53
  throw new Error(`Template not found: ${srcDir}`);
44
54
  }
@@ -66,14 +76,33 @@ async function copyTemplate(srcDir, destDir, projectName) {
66
76
  }
67
77
  }
68
78
 
69
- // Build the project's root package.json dynamically (pnpm: NO "workspaces" field).
70
- function buildRootPackageJson(projectName) {
79
+ // ---------------------------------------------------------------------------
80
+ // Dynamic root files (pnpm + Turborepo standard).
81
+ // ---------------------------------------------------------------------------
82
+
83
+ // Build the project's root package.json (pnpm: NO "workspaces" field).
84
+ function buildRootPackageJson(projectName, hasShared) {
71
85
  return {
72
86
  name: projectName,
73
87
  version: '0.1.0',
74
88
  private: true,
75
- engines: { node: '>=18' },
76
- packageManager: 'pnpm@9.0.0',
89
+ packageManager: 'pnpm@9.12.0',
90
+ engines: { node: '>=20' },
91
+ scripts: {
92
+ bootstrap: 'node scripts/bootstrap.mjs',
93
+ build: 'turbo run build',
94
+ dev: 'turbo run dev',
95
+ lint: 'turbo run lint',
96
+ typecheck: 'turbo run typecheck',
97
+ test: 'turbo run test',
98
+ ...(hasShared
99
+ ? { 'shared:build': `turbo run build --filter=${SHARED_PACKAGE_NAME}` }
100
+ : {}),
101
+ },
102
+ devDependencies: {
103
+ turbo: '^2.3.3',
104
+ typescript: '^5.8.3',
105
+ },
77
106
  };
78
107
  }
79
108
 
@@ -85,6 +114,181 @@ function buildPnpmWorkspaceYaml(createdAppDirs) {
85
114
  return `packages:\n${lines.join('\n')}\n`;
86
115
  }
87
116
 
117
+ const TURBO_JSON = {
118
+ $schema: 'https://turbo.build/schema.json',
119
+ tasks: {
120
+ build: {
121
+ dependsOn: ['^build'],
122
+ outputs: ['dist/**', '.next/**', '!.next/cache/**', 'build/**'],
123
+ },
124
+ lint: { dependsOn: ['^build'] },
125
+ typecheck: { dependsOn: ['^build'] },
126
+ test: { dependsOn: ['^build'], outputs: ['coverage/**'] },
127
+ dev: { cache: false, persistent: true },
128
+ },
129
+ };
130
+
131
+ const TSCONFIG_BASE = {
132
+ $schema: 'https://json.schemastore.org/tsconfig',
133
+ compilerOptions: {
134
+ target: 'ESNext',
135
+ module: 'ESNext',
136
+ moduleResolution: 'Bundler',
137
+ lib: ['ESNext'],
138
+ strict: true,
139
+ esModuleInterop: true,
140
+ allowSyntheticDefaultImports: true,
141
+ forceConsistentCasingInFileNames: true,
142
+ skipLibCheck: true,
143
+ resolveJsonModule: true,
144
+ isolatedModules: true,
145
+ declaration: true,
146
+ baseUrl: '.',
147
+ paths: { '@repo/*': ['packages/*/src'] },
148
+ },
149
+ exclude: ['node_modules', 'dist', 'build'],
150
+ };
151
+
152
+ const GITIGNORE = `# Dependencies
153
+ node_modules/
154
+ .pnpm-store/
155
+
156
+ # Turborepo
157
+ .turbo/
158
+
159
+ # Build outputs
160
+ dist/
161
+ build/
162
+ out/
163
+ .next/
164
+ *.tsbuildinfo
165
+
166
+ # Expo / React Native
167
+ .expo/
168
+ web-build/
169
+ .metro-health-check*
170
+
171
+ # Environment variables
172
+ .env
173
+ .env.*
174
+ !.env.example
175
+ !.env.*.example
176
+
177
+ # Testing
178
+ coverage/
179
+
180
+ # Logs
181
+ *.log
182
+ npm-debug.log*
183
+ pnpm-debug.log*
184
+ yarn-error.log*
185
+
186
+ # OS
187
+ .DS_Store
188
+
189
+ # IDE
190
+ .idea/
191
+ .vscode/
192
+ *.iml
193
+ `;
194
+
195
+ const NPMRC = `# Hoist node_modules so Metro (Expo / React Native) resolves packages and
196
+ # workspace dependencies (@repo/*) correctly. Harmless for web/back-end apps.
197
+ node-linker=hoisted
198
+
199
+ # Keep peer-dependency resolution lenient — the RN ecosystem has many loose
200
+ # peer ranges that would otherwise fail install.
201
+ strict-peer-dependencies=false
202
+ `;
203
+
204
+ // One-command bootstrap, written to the generated repo as scripts/bootstrap.mjs.
205
+ // Run it (`pnpm bootstrap`) AFTER dropping the real stack source in: it installs
206
+ // deps, builds @repo/shared, then verifies the apps can resolve it.
207
+ // NOTE: not named "setup" because `pnpm setup` is a reserved pnpm built-in.
208
+ const SETUP_MJS = `// bootstrap.mjs — one-command bootstrap for this monorepo.
209
+ //
210
+ // Run it after you have dropped the real source into apps/* :
211
+ //
212
+ // pnpm bootstrap
213
+ //
214
+ // It installs dependencies, builds @repo/shared (so its dist/ exists), and
215
+ // verifies every app that depends on it can resolve \`@repo/shared\`.
216
+
217
+ import { spawnSync } from 'node:child_process';
218
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
219
+ import { createRequire } from 'node:module';
220
+ import path from 'node:path';
221
+ import { fileURLToPath } from 'node:url';
222
+
223
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
224
+ const SHARED = '@repo/shared';
225
+
226
+ function sh(command) {
227
+ console.log(\`\\n$ \${command}\`);
228
+ const r = spawnSync(command, { cwd: root, stdio: 'inherit', shell: true });
229
+ if (r.status !== 0) {
230
+ console.error(\`\\n\\u2717 Failed: \${command}\`);
231
+ process.exit(r.status ?? 1);
232
+ }
233
+ }
234
+
235
+ // 1) Install all workspace dependencies.
236
+ sh('pnpm install');
237
+
238
+ // 2) Build the shared package (if present) so apps can import its dist output.
239
+ const sharedDir = path.join(root, 'packages', 'shared');
240
+ const hasShared = existsSync(sharedDir);
241
+ if (hasShared) {
242
+ sh(\`pnpm --filter \${SHARED} build\`);
243
+ } else {
244
+ console.log('\\n\\u2022 No packages/shared in this repo — skipping shared build.');
245
+ }
246
+
247
+ // 3) Verify each app that depends on @repo/shared can actually resolve it.
248
+ if (hasShared) {
249
+ const appsDir = path.join(root, 'apps');
250
+ const apps = existsSync(appsDir)
251
+ ? readdirSync(appsDir, { withFileTypes: true }).filter((e) => e.isDirectory())
252
+ : [];
253
+
254
+ let failures = 0;
255
+ for (const app of apps) {
256
+ const pkgPath = path.join(appsDir, app.name, 'package.json');
257
+ if (!existsSync(pkgPath)) continue;
258
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
259
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
260
+ if (!deps[SHARED]) continue;
261
+
262
+ const require = createRequire(path.join(appsDir, app.name, 'noop.js'));
263
+ try {
264
+ require.resolve(SHARED);
265
+ console.log(\`\\n\\u2713 apps/\${app.name} resolves \${SHARED}\`);
266
+ } catch {
267
+ console.error(\`\\n\\u2717 apps/\${app.name} cannot resolve \${SHARED}\`);
268
+ failures++;
269
+ }
270
+ }
271
+
272
+ if (failures > 0) {
273
+ console.error(\`\\n\${failures} app(s) cannot resolve \${SHARED}. Check the \"\${SHARED}\" dependency and that its build produced dist/.\`);
274
+ process.exit(1);
275
+ }
276
+ }
277
+
278
+ console.log('\\n\\u2713 Setup complete. Start developing with: pnpm dev');
279
+ `;
280
+
281
+ // ---------------------------------------------------------------------------
282
+
283
+ // Add "@repo/shared": "workspace:*" to a JS/TS app's package.json (if present).
284
+ async function wireSharedDependency(appPkgPath) {
285
+ if (!(await fse.pathExists(appPkgPath))) return false;
286
+ const pkg = await fse.readJson(appPkgPath);
287
+ pkg.dependencies = { ...(pkg.dependencies ?? {}), [SHARED_PACKAGE_NAME]: 'workspace:*' };
288
+ await fse.writeJson(appPkgPath, pkg, { spaces: 2 });
289
+ return true;
290
+ }
291
+
88
292
  export async function generate(plan, repoRoot, cwd = process.cwd()) {
89
293
  const { projectName, selections } = plan;
90
294
  const projectRoot = path.resolve(cwd, projectName);
@@ -107,27 +311,54 @@ export async function generate(plan, repoRoot, cwd = process.cwd()) {
107
311
  }
108
312
 
109
313
  // 2) packages/shared is CONDITIONAL: >= 2 selections with lang === 'js'.
110
- const jsCount = selections.filter((s) => s.option.lang === 'js').length;
111
- if (jsCount >= 2) {
314
+ const jsSelections = selections.filter((s) => s.option.lang === 'js');
315
+ const hasShared = jsSelections.length >= 2;
316
+ if (hasShared) {
112
317
  const srcDir = path.resolve(repoRoot, SHARED_TEMPLATE_PATH);
113
318
  const destDir = path.resolve(projectRoot, SHARED_OUTPUT_DIR);
114
319
  await copyTemplate(srcDir, destDir, projectName);
115
- logs.push(`✓ shared → ${SHARED_OUTPUT_DIR} (${jsCount} JS/TS apps)`);
320
+ logs.push(`✓ shared → ${SHARED_OUTPUT_DIR} (${jsSelections.length} JS/TS apps)`);
321
+
322
+ // Wire each JS/TS app to depend on the shared package.
323
+ for (const { group } of jsSelections) {
324
+ const appPkg = path.join(projectRoot, group.outputDir, 'package.json');
325
+ if (await wireSharedDependency(appPkg)) {
326
+ logs.push(` ↳ ${group.outputDir} depends on ${SHARED_PACKAGE_NAME} (workspace:*)`);
327
+ }
328
+ }
116
329
  } else {
117
- logs.push(`• Skipping shared: not enough JS/TS apps (need >= 2, have ${jsCount}).`);
330
+ logs.push(`• Skipping shared: not enough JS/TS apps (need >= 2, have ${jsSelections.length}).`);
118
331
  }
119
332
 
120
- // 3) Generate the root package.json + pnpm-workspace.yaml dynamically.
121
- const rootPkg = buildRootPackageJson(projectName);
122
- await fse.writeJson(path.join(projectRoot, 'package.json'), rootPkg, { spaces: 2 });
333
+ // 3) Generate the root config files dynamically.
334
+ await fse.writeJson(
335
+ path.join(projectRoot, 'package.json'),
336
+ buildRootPackageJson(projectName, hasShared),
337
+ { spaces: 2 },
338
+ );
123
339
  logs.push('✓ package.json');
124
340
 
125
- const wsEntries = [...createdAppDirs, 'packages/*'];
126
341
  await fse.writeFile(
127
342
  path.join(projectRoot, 'pnpm-workspace.yaml'),
128
343
  buildPnpmWorkspaceYaml(createdAppDirs),
129
344
  );
130
- logs.push(`✓ pnpm-workspace.yaml (packages: ${wsEntries.join(', ')})`);
345
+ logs.push(`✓ pnpm-workspace.yaml (packages: ${[...createdAppDirs, 'packages/*'].join(', ')})`);
346
+
347
+ await fse.writeJson(path.join(projectRoot, 'turbo.json'), TURBO_JSON, { spaces: 2 });
348
+ logs.push('✓ turbo.json');
349
+
350
+ await fse.writeJson(path.join(projectRoot, 'tsconfig.base.json'), TSCONFIG_BASE, { spaces: 2 });
351
+ logs.push('✓ tsconfig.base.json');
352
+
353
+ await fse.writeFile(path.join(projectRoot, '.gitignore'), GITIGNORE);
354
+ logs.push('✓ .gitignore');
355
+
356
+ await fse.writeFile(path.join(projectRoot, '.npmrc'), NPMRC);
357
+ logs.push('✓ .npmrc');
358
+
359
+ await fse.ensureDir(path.join(projectRoot, 'scripts'));
360
+ await fse.writeFile(path.join(projectRoot, 'scripts', 'bootstrap.mjs'), SETUP_MJS);
361
+ logs.push('✓ scripts/bootstrap.mjs (run `pnpm bootstrap` after dropping source in)');
131
362
 
132
363
  return { projectRoot, logs };
133
364
  }
package/src/registry.js CHANGED
@@ -43,9 +43,11 @@ export const GROUPS = [
43
43
  },
44
44
  ];
45
45
 
46
- // Path to the shared template folder (only copied when >= 2 selections have lang === 'js').
46
+ // Shared package config. Only copied when >= 2 selections have lang === 'js'.
47
+ // Uses the `@repo/*` scope so it matches the `paths` mapping in tsconfig.base.json.
47
48
  export const SHARED_TEMPLATE_PATH = 'templates/shared';
48
49
  export const SHARED_OUTPUT_DIR = 'packages/shared';
50
+ export const SHARED_PACKAGE_NAME = '@repo/shared';
49
51
 
50
52
  // Quickly look up a single option by (groupId, optionId).
51
53
  export function findOption(groupId, optionId) {
@@ -1,11 +1,26 @@
1
- # Placeholder: shared (packages/shared)
1
+ # @repo/shared (packages/shared)
2
2
 
3
- This is a **placeholder** for the shared **JS/TS** package: shared types + api client.
3
+ Shared data contracts (types, enums, API URLs, schemas) consumed by every JS/TS
4
+ app in the monorepo via `import { ... } from '@repo/shared'`.
4
5
 
5
- Drop the standard shared source (types, api client, ...) for the JS/TS apps here. Conventions:
6
+ ## Layout
7
+
8
+ - `src/index.ts` — barrel file. Re-export everything you want apps to consume.
9
+ - `tsup.config.ts` — builds `dist/` (ESM + CJS + `.d.ts`).
10
+ - `tsconfig.json` — extends the root `tsconfig.base.json`.
11
+
12
+ ## How it is wired
13
+
14
+ - Built with **tsup** (`pnpm --filter @repo/shared build`); output goes to `dist/`.
15
+ - Each JS/TS app gets `"@repo/shared": "workspace:*"` added automatically when
16
+ this package is generated.
17
+ - Turborepo runs `^build` first, so `@repo/shared` is built before any app that
18
+ depends on it.
19
+
20
+ ## Conventions for template authors
6
21
 
7
22
  - Name the template's `package.json` as **`_package.json`** (the CLI renames it on generate).
8
- - Wherever you need the project name, write `{{PROJECT_NAME}}` — the CLI replaces it with the real name.
23
+ - Wherever you need the project name, write `{{PROJECT_NAME}}` — the CLI replaces it.
9
24
 
10
- > This package is **created only** when the project has **>= 2** selections with `lang === 'js'`.
11
- > Otherwise the CLI skips it and prints a notice.
25
+ > This package is **created only** when the project has **>= 2** selections with
26
+ > `lang === 'js'`. Otherwise the CLI skips it and prints a notice.
@@ -1,8 +1,30 @@
1
1
  {
2
- "name": "@{{PROJECT_NAME}}/shared",
2
+ "name": "@repo/shared",
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
- "description": "Shared types + api client for {{PROJECT_NAME}}",
6
- "main": "index.js",
7
- "type": "module"
5
+ "description": "Shared data contracts (types, enums, API URLs) for {{PROJECT_NAME}}",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup",
22
+ "dev": "tsup --watch",
23
+ "typecheck": "tsc --noEmit",
24
+ "clean": "rm -rf dist"
25
+ },
26
+ "devDependencies": {
27
+ "tsup": "^8.3.5",
28
+ "typescript": "^5.8.3"
29
+ }
8
30
  }
@@ -0,0 +1,12 @@
1
+ // @repo/shared — shared data contracts for {{PROJECT_NAME}}.
2
+ //
3
+ // Placeholder. Drop the real shared code here (types, enums, API URLs, zod
4
+ // schemas, etc.) and re-export it from this barrel file. Everything exported
5
+ // here is published to the other apps via `import { ... } from '@repo/shared'`.
6
+
7
+ export const PROJECT_NAME = '{{PROJECT_NAME}}';
8
+
9
+ export interface ApiResponse<T> {
10
+ data: T;
11
+ message?: string;
12
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist"
6
+ },
7
+ "include": ["src/**/*"]
8
+ }
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entry: ['src/index.ts'],
5
+ format: ['esm', 'cjs'],
6
+ dts: true,
7
+ clean: true,
8
+ sourcemap: true,
9
+ });
@@ -1,4 +0,0 @@
1
- // Shared package for {{PROJECT_NAME}}.
2
- // Placeholder — drop the real shared types + api client here.
3
-
4
- export const PROJECT_NAME = '{{PROJECT_NAME}}';