tackl 2.0.0 → 2.2.0

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.
Files changed (3) hide show
  1. package/README.md +4 -0
  2. package/bin/tackl.js +54 -17
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -11,6 +11,10 @@ bunx tackl # or: bun create tackl / npm create tackl@latest
11
11
 
12
12
  You'll be asked which CMS to use — **DatoCMS**, **Sanity**, or **None** — and the scaffold is pruned to match: only the chosen adapter, its dependencies, its docs, and its `.env.example` block survive.
13
13
 
14
+ Choosing **Sanity** sets the whole thing up: an embedded Studio at `/studio`, example schemas, and draft-mode preview routes ship in the scaffold, and the CLI offers to run `sanity init` for you — logging in, creating/linking a project, and writing your `.env`. Scaffold → `bun run dev` → `/studio` → start editing.
15
+
16
+ On your first `bun run dev`, a **setup wizard** greets you in the browser: it walks through every theme token (colours, spacing, motion, fonts, the type scale), writes your answers into `src/theme`, then deletes itself — skip it to keep the starter defaults. The scaffold commit the CLI makes is your restore point if you ever want to run it again.
17
+
14
18
  ## What it does
15
19
 
16
20
  1. Verifies the directory is empty (an existing `.git`, `.gitignore`, `README.md` or `LICENSE` is fine — your repo's `.git` and remote are kept)
package/bin/tackl.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { execSync } from 'child_process';
3
- import fs from 'fs';
4
- import os from 'os';
5
- import path from 'path';
2
+ import { execSync } from 'node:child_process';
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
6
  import pc from 'picocolors';
7
7
  import prompts from 'prompts';
8
8
  import * as tar from 'tar';
@@ -104,16 +104,21 @@ const stripDeps = (root, deps) => {
104
104
  delete json.dependencies?.[d];
105
105
  delete json.devDependencies?.[d];
106
106
  }
107
- fs.writeFileSync(p, JSON.stringify(json, null, '\t') + '\n');
107
+ fs.writeFileSync(p, `${JSON.stringify(json, null, '\t')}\n`);
108
108
  };
109
109
 
110
+ // NOTE • Everything that exists solely for the embedded Sanity Studio
111
+ const SANITY_STUDIO_PATHS = ['app/(studio)', 'app/api/draft-mode', 'sanity.config.ts', 'sanity'];
112
+ const SANITY_DEPS = ['next-sanity', 'sanity', '@sanity/vision'];
113
+
110
114
  const CMS = {
111
115
  dato: {
112
116
  title: 'DatoCMS',
113
117
  prune: root => {
114
118
  rmrf(root, 'src/cms/sanity');
115
119
  rmrf(root, 'docs/Sanity');
116
- stripDeps(root, ['next-sanity']);
120
+ for (const p of SANITY_STUDIO_PATHS) rmrf(root, p);
121
+ stripDeps(root, SANITY_DEPS);
117
122
  stripEnvBlock(root, '# CMS — Sanity');
118
123
  },
119
124
  },
@@ -138,7 +143,8 @@ const CMS = {
138
143
  rmrf(root, 'docs/Sanity');
139
144
  rmrf(root, 'docs/DatoCMS');
140
145
  rmrf(root, 'docs/CMS.md');
141
- stripDeps(root, ['next-sanity', '@datocms/cda-client', 'react-datocms', 'graphql', 'graphql-tag']);
146
+ for (const p of SANITY_STUDIO_PATHS) rmrf(root, p);
147
+ stripDeps(root, [...SANITY_DEPS, '@datocms/cda-client', 'react-datocms', 'graphql', 'graphql-tag']);
142
148
  stripEnvBlock(root, '# CMS — Sanity');
143
149
  stripEnvBlock(root, '# CMS — DatoCMS');
144
150
  },
@@ -182,7 +188,9 @@ ${pc.bold('Repository:')} https://github.com/${OWNER}/${REPO}
182
188
  const targetDir = process.cwd();
183
189
 
184
190
  if (!dirIsScaffoldable(targetDir)) {
185
- log.error('This directory is not empty. Run tackl inside an empty directory (a .git folder, .gitignore, README.md and LICENSE are fine).');
191
+ log.error(
192
+ 'This directory is not empty. Run tackl inside an empty directory (a .git folder, .gitignore, README.md and LICENSE are fine).'
193
+ );
186
194
  process.exit(1);
187
195
  }
188
196
 
@@ -219,10 +227,11 @@ ${pc.bold('Repository:')} https://github.com/${OWNER}/${REPO}
219
227
 
220
228
  const cms = cmsFlag || answers.cms;
221
229
  const rawName = flagValue('--name') || path.basename(targetDir);
222
- const projectName = rawName
223
- .toLowerCase()
224
- .replace(/[^a-z0-9\-._]/g, '-')
225
- .replace(/^[-.]+|[-.]+$/g, '') || 'tackl-app';
230
+ const projectName =
231
+ rawName
232
+ .toLowerCase()
233
+ .replace(/[^a-z0-9\-._]/g, '-')
234
+ .replace(/^[-.]+|[-.]+$/g, '') || 'tackl-app';
226
235
  const doInstall = !args.includes('--no-install');
227
236
  const doGit = !args.includes('--no-git');
228
237
  const hadGit = fs.existsSync(path.join(targetDir, '.git'));
@@ -294,7 +303,7 @@ ${pc.bold('Repository:')} https://github.com/${OWNER}/${REPO}
294
303
  json.version = '0.1.0';
295
304
  delete json.repository;
296
305
  delete json.author;
297
- fs.writeFileSync(pkgPath, JSON.stringify(json, null, '\t') + '\n');
306
+ fs.writeFileSync(pkgPath, `${JSON.stringify(json, null, '\t')}\n`);
298
307
  log.ok('package.json updated.');
299
308
  } catch (e) {
300
309
  log.warn(`Could not update package.json: ${e.message}`);
@@ -330,11 +339,39 @@ ${pc.bold('Repository:')} https://github.com/${OWNER}/${REPO}
330
339
  log.info('Skipped dependency install.');
331
340
  }
332
341
 
333
- console.log('\n' + pc.bold(pc.green('Success!')) + ' Project scaffolded.\n');
342
+ // SECTION • Sanity project link
343
+ // NOTE • A Sanity scaffold isn't usable until a project id exists —
344
+ // sanity's own init logs in, creates/links a project and writes .env.
345
+ let sanityLinked = false;
346
+ if (cms === 'sanity' && process.stdout.isTTY) {
347
+ const { link } = await prompts({
348
+ type: 'confirm',
349
+ name: 'link',
350
+ message: 'Link a Sanity project now? (opens a browser login, writes .env)',
351
+ initial: true,
352
+ });
353
+ if (link) {
354
+ try {
355
+ execSync('bunx sanity@latest init --env .env', { cwd: targetDir, stdio: 'inherit' });
356
+ sanityLinked = true;
357
+ log.ok('Sanity project linked!');
358
+ } catch (e) {
359
+ log.warn(`sanity init did not complete: ${e.message}`);
360
+ }
361
+ }
362
+ }
363
+
364
+ console.log(`\n${pc.bold(pc.green('Success!'))} Project scaffolded.\n`);
334
365
  console.log(' Next steps:');
335
- console.log(' 1) cp .env.example .env # then fill in your values');
336
- if (!doInstall) console.log(' 2) bun install');
337
- console.log(` ${doInstall ? '2' : '3'}) bun run dev`);
366
+ let step = 1;
367
+ if (cms === 'sanity' && !sanityLinked) {
368
+ console.log(` ${step++}) bunx sanity init --env .env # create/link your Sanity project`);
369
+ } else if (cms !== 'sanity') {
370
+ console.log(` ${step++}) cp .env.example .env # then fill in your values`);
371
+ }
372
+ if (!doInstall) console.log(` ${step++}) bun install`);
373
+ console.log(` ${step++}) bun run dev # a first-run setup wizard walks you through your theme`);
374
+ if (cms === 'sanity') console.log(` ${step}) open http://localhost:3000/studio to start editing content`);
338
375
  }
339
376
 
340
377
  main().catch(e => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tackl",
3
- "version": "2.0.0",
3
+ "version": "2.2.0",
4
4
  "description": "Scaffold a Tackl project into the current directory — Next.js, styled-components, Waffl grid, DatoCMS/Sanity adapters",
5
5
  "type": "module",
6
6
  "bin": {