swatchkit 0.4.1 → 0.6.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 +20 -3
  2. package/build.js +61 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -36,7 +36,7 @@ my-project/
36
36
  │ ├── colors.html
37
37
  │ ├── typography.html
38
38
  │ └── ...
39
- └── public/
39
+ └── dist/
40
40
  └── swatchkit/ # Built pattern library
41
41
  └── index.html
42
42
  ```
@@ -207,7 +207,7 @@ Copies "blueprints" into your project to get you started.
207
207
  * **`swatchkit/`**: Sets up the documentation structure and layout.
208
208
 
209
209
  ### 2. `swatchkit` (Build Process)
210
- Compiles your documentation site into `public/swatchkit/`.
210
+ Compiles your documentation site into `dist/swatchkit/`.
211
211
 
212
212
  1. **Reads JSON Tokens**: Scans `tokens/*.json` and calculates fluid typography/spacing.
213
213
  2. **Generates CSS**: Creates `css/tokens.css`. **Do not edit this file**; it is overwritten every build.
@@ -249,7 +249,7 @@ swatchkit [command] [options]
249
249
  | `--watch` | `-w` | Watch files and rebuild on change. |
250
250
  | `--config` | `-c` | Path to config file. |
251
251
  | `--input` | `-i` | Pattern directory (Default: `swatchkit/`). |
252
- | `--outDir` | `-o` | Output directory (Default: `public/swatchkit`). |
252
+ | `--outDir` | `-o` | Output directory (Default: `dist/swatchkit`). |
253
253
  | `--force` | `-f` | Overwrite layout file during init. |
254
254
 
255
255
  ## Configuration
@@ -275,6 +275,23 @@ module.exports = {
275
275
  };
276
276
  ```
277
277
 
278
+ ## Using with a Framework
279
+
280
+ SwatchKit outputs to `dist/swatchkit/` by default. If your framework (Vite, Astro, etc.) cleans the `dist/` directory during its build, run SwatchKit **after** your framework build:
281
+
282
+ ```json
283
+ {
284
+ "scripts": {
285
+ "build": "vite build && swatchkit",
286
+ "dev": "vite dev & swatchkit -w"
287
+ }
288
+ }
289
+ ```
290
+
291
+ In watch mode, SwatchKit detects when its output directory is deleted by an external tool and automatically rebuilds.
292
+
293
+ SwatchKit only ever writes inside its own output subdirectory — it will never modify or delete other files in `dist/`.
294
+
278
295
  ## Acknowledgements
279
296
 
280
297
  The CSS compositions included by default in SwatchKit are adapted from [Every Layout](https://every-layout.dev/) by Heydon Pickering and Andy Bell. Highly recommend their documentation for a deep dive into their brilliant CSS techniques.
package/build.js CHANGED
@@ -2,7 +2,7 @@
2
2
  const fs = require("fs");
3
3
  const path = require("path");
4
4
  const chokidar = require("chokidar");
5
- const { processTokens } = require("./src/tokens");
5
+ const { processTokens, generateTokenUtilities } = require("./src/tokens");
6
6
  const { generateTokenSwatches } = require("./src/generators");
7
7
 
8
8
  /**
@@ -110,12 +110,12 @@ function resolveSettings(cliOptions, fileConfig) {
110
110
  const swatchkitDir = findSwatchkitDir();
111
111
 
112
112
  // Output Dir
113
- // Default: public/swatchkit
113
+ // Default: dist/swatchkit
114
114
  const outDir = cliOptions.outDir
115
115
  ? path.resolve(cwd, cliOptions.outDir)
116
116
  : fileConfig.outDir
117
117
  ? path.resolve(cwd, fileConfig.outDir)
118
- : path.join(cwd, "public/swatchkit");
118
+ : path.join(cwd, "dist/swatchkit");
119
119
 
120
120
  // CSS directory - where tokens.css and user's main.css live
121
121
  // Default: css/ at project root
@@ -298,7 +298,11 @@ function runInit(settings, options) {
298
298
  // processTokens now expects the folder where tokens.css should live
299
299
  // We pass settings.cssDir, but processTokens internally joins 'tokens.css'
300
300
  // So we need to point it to css/global
301
- processTokens(settings.tokensDir, path.join(settings.cssDir, "global"));
301
+ const tokensContext = processTokens(settings.tokensDir, path.join(settings.cssDir, "global"));
302
+
303
+ if (tokensContext) {
304
+ generateTokenUtilities(tokensContext, path.join(settings.cssDir, "utilities"));
305
+ }
302
306
 
303
307
  const targetLayout = settings.projectLayout;
304
308
 
@@ -424,6 +428,24 @@ ${scriptContent}
424
428
  return swatches;
425
429
  }
426
430
 
431
+ function validateOutDir(outDir) {
432
+ const cwd = process.cwd();
433
+ const relative = path.relative(cwd, outDir);
434
+
435
+ if (
436
+ !relative || // outDir === cwd
437
+ relative === '..' || // above cwd
438
+ relative.startsWith('../') || // above cwd
439
+ path.isAbsolute(relative) || // different drive/root
440
+ relative.split(path.sep).length < 2 // top-level dir like "dist" with no subfolder
441
+ ) {
442
+ console.error(
443
+ `[SwatchKit] Refusing to clean outDir "${outDir}" — must be a subdirectory at least 2 levels deep (e.g., dist/swatchkit).`,
444
+ );
445
+ process.exit(1);
446
+ }
447
+ }
448
+
427
449
  function build(settings) {
428
450
  console.log(`[SwatchKit] Starting build...`);
429
451
  console.log(` Source: ${settings.swatchkitDir}`);
@@ -438,7 +460,13 @@ function build(settings) {
438
460
  process.exit(1);
439
461
  }
440
462
 
441
- // 2. Ensure dist directories exist
463
+ // 2. Clean previous output (only our subdirectory, never the parent)
464
+ validateOutDir(settings.outDir);
465
+ if (fs.existsSync(settings.outDir)) {
466
+ fs.rmSync(settings.outDir, { recursive: true });
467
+ }
468
+
469
+ // 3. Ensure dist directories exist
442
470
  [settings.outDir, settings.distCssDir, settings.distJsDir].forEach((dir) => {
443
471
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
444
472
  });
@@ -446,7 +474,12 @@ function build(settings) {
446
474
  // 2.5 Process Tokens
447
475
  console.log("Reading JSON tokens (tokens/*.json)...");
448
476
  // Output tokens.css to css/global/tokens.css
449
- processTokens(settings.tokensDir, path.join(settings.cssDir, "global"));
477
+ const tokensContext = processTokens(settings.tokensDir, path.join(settings.cssDir, "global"));
478
+
479
+ // Generate Utilities to css/utilities/tokens.css
480
+ if (tokensContext) {
481
+ generateTokenUtilities(tokensContext, path.join(settings.cssDir, "utilities"));
482
+ }
450
483
 
451
484
  // 2.6 Generate token display HTML from JSON
452
485
  const tokensUiDir = path.join(settings.swatchkitDir, "tokens");
@@ -674,7 +707,7 @@ function build(settings) {
674
707
 
675
708
  // --- 6. Watch Logic ---
676
709
  function watch(settings) {
677
- const watchPaths = [
710
+ const sourcePaths = [
678
711
  settings.swatchkitDir,
679
712
  settings.tokensDir,
680
713
  settings.projectLayout,
@@ -683,30 +716,48 @@ function watch(settings) {
683
716
 
684
717
  console.log("[SwatchKit] Watch mode enabled.");
685
718
  console.log("Watching for changes in:");
686
- watchPaths.forEach((p) => console.log(` - ${p}`));
719
+ sourcePaths.forEach((p) => console.log(` - ${p}`));
720
+ console.log(` Polling: ${settings.outDir} (rebuild if deleted by external tools)`);
687
721
 
688
722
  let buildTimeout;
723
+ let isRebuilding = false;
724
+
689
725
  const rebuild = () => {
690
726
  if (buildTimeout) clearTimeout(buildTimeout);
691
727
  buildTimeout = setTimeout(() => {
692
728
  try {
729
+ isRebuilding = true;
693
730
  console.log("[SwatchKit] Change detected. Rebuilding...");
694
731
  build(settings);
695
732
  } catch (e) {
696
733
  console.error("[SwatchKit] Build failed:", e.message);
734
+ } finally {
735
+ isRebuilding = false;
697
736
  }
698
737
  }, 100); // 100ms debounce
699
738
  };
700
739
 
701
- const watcher = chokidar.watch(watchPaths, {
740
+ // Watch source files for changes
741
+ const sourceWatcher = chokidar.watch(sourcePaths, {
702
742
  ignored: /(^|[\/\\])\../, // ignore dotfiles
703
743
  persistent: true,
704
744
  ignoreInitial: true,
705
745
  });
706
746
 
707
- watcher.on("all", (event, path) => {
747
+ sourceWatcher.on("all", () => {
708
748
  rebuild();
709
749
  });
750
+
751
+ // Poll for output directory deletion (e.g., when a framework build wipes dist/).
752
+ // We use polling instead of a chokidar watcher on the output dir to avoid
753
+ // infinite rebuild loops — since our own build deletes and recreates the
754
+ // output directory, an event-based watcher would retrigger endlessly.
755
+ setInterval(() => {
756
+ if (!isRebuilding && !fs.existsSync(settings.outDir)) {
757
+ console.log("[SwatchKit] Output directory was deleted. Rebuilding...");
758
+ rebuild();
759
+ }
760
+ }, 2000);
710
761
  }
711
762
 
712
763
  // --- Main Execution ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "swatchkit",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "A lightweight tool for creating HTML pattern libraries.",
5
5
  "main": "build.js",
6
6
  "bin": {