@svgforge/svgforge-cli 2.0.0 → 2.0.2-unstable.202609132246.c961c8e

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/LICENSE CHANGED
@@ -1,7 +1,6 @@
1
1
  MIT License
2
2
 
3
3
  Copyright (c) 2026 Felix Müller
4
- Based on svg-sprite — Copyright © 2018 Joschi Kuphal
5
4
 
6
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
7
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -103,6 +103,7 @@ The test suite spawns the CLI as a child process against fixtures in `test/fixtu
103
103
  ## License
104
104
 
105
105
  [MIT](LICENSE) © Felix Müller
106
+
106
107
  [npm-url]: https://www.npmjs.com/package/@svgforge/svgforge-cli
107
108
 
108
109
  [npm-image]: https://img.shields.io/npm/v/@svgforge/svgforge-cli?logo=npm&logoColor=fff
package/bin/svgforge.js CHANGED
@@ -3,15 +3,13 @@
3
3
  /**
4
4
  Svgforge is a Node.js module for creating SVG sprites
5
5
 
6
- Based on the command line interface originally written for svg-sprite
6
+ Some code based on the command line interface originally written for svg-sprite
7
7
  by Joschi Kuphal — this is a standalone fork/package of that CLI.
8
8
 
9
- @see https://github.com/joeda1/svgforge-cli
10
- @author Joschi Kuphal <joschi@kuphal.net> (https://github.com/jkphl)
11
- @author Felix Müller
12
- @copyright © 2018 Joschi Kuphal
9
+ @see https://github.com/svgforge/svgforge-cli
10
+ @author Felix Müller (https://github.com/joeda1)
13
11
  @copyright © 2026 Felix Müller
14
- @license MIT https://github.com/joeda1/svgforge-cli/blob/main/LICENSE
12
+ @license MIT https://github.com/svgforge/svgforge-cli/blob/main/LICENSE
15
13
  */
16
14
 
17
15
  import fs from 'node:fs';
@@ -471,6 +469,40 @@ function loadVariables(cfg) {
471
469
  }
472
470
  }
473
471
 
472
+ /**
473
+ Resolve the output directories that must not be re-ingested as source files
474
+
475
+ Generated artifacts (sprites, stylesheets, examples) live inside the configured
476
+ destination tree. When an input glob covers that tree (e.g. a recursive
477
+ SVG glob), a sprite written by a previous run would otherwise be fed back
478
+ in as a shape with an id derived from its output path (e.g.
479
+ "assets--OUT--stack--svg--sprite").
480
+
481
+ @param {SpriterConfig} cfg Spriter configuration
482
+ @returns {string[]} Absolute output root directories to exclude from the input
483
+ */
484
+ function getExcludedInputRoots(cfg) {
485
+ const destRoot = path.resolve(cfg.dest || '.');
486
+ const roots = new Set([destRoot]);
487
+
488
+ // When the destination defaults to the current directory every glob would
489
+ // land inside it, so restrict the exclusion to the mode specific output
490
+ // directories instead.
491
+ if (destRoot === process.cwd()) {
492
+ roots.clear();
493
+
494
+ for (const mode of MODES) {
495
+ const modeDest = cfg.mode?.[mode]?.dest;
496
+
497
+ if (modeDest) {
498
+ roots.add(path.resolve(destRoot, modeDest));
499
+ }
500
+ }
501
+ }
502
+
503
+ return [...roots];
504
+ }
505
+
474
506
  /**
475
507
  Run the command line interface
476
508
 
@@ -493,19 +525,41 @@ async function main() {
493
525
  loadVariables(config);
494
526
 
495
527
  const spriter = new SVGSpriter(config);
496
- const files = argv._.flatMap(filePattern => fs.globSync(filePattern).toSorted((a, b) => b.localeCompare(a)));
497
528
 
498
- for (const filePattern of files) {
499
- // Glob >= 9 returns paths without the "./" prefix, so detect relative
500
- // patterns from the original glob result to preserve directory structure
501
- // in the shape identifiers (e.g. "nested/leaf.svg" -> "nested--leaf")
502
- const isRelative = !path.isAbsolute(filePattern) && !filePattern.startsWith('../');
529
+ // Glob (>= 9, incl. fs.globSync) returns paths without the "./" segment, so
530
+ // the base directory marker has to be detected on the original pattern. A
531
+ // literal "/./" segment marks the directory from which shape ID traversal
532
+ // should start (e.g. "assets/./**/*.svg" yields "path--to--source" instead
533
+ // of "assets--path--to--source").
534
+ const matchedFiles = argv._.flatMap(pattern => {
535
+ const marker = pattern.lastIndexOf('./');
536
+ const baseDepth = marker === -1 ? 0 : pattern.slice(0, marker).split('/').filter(Boolean).length;
537
+ return fs.globSync(pattern)
538
+ .toSorted((a, b) => b.localeCompare(a))
539
+ .map(file => ({baseDepth, file}));
540
+ });
541
+ const excludedInputRoots = getExcludedInputRoots(config);
542
+
543
+ for (const {baseDepth, file: filePattern} of matchedFiles) {
503
544
  const file = path.resolve(filePattern);
545
+
546
+ // Skip files inside the output tree so previously generated artifacts are
547
+ // not processed as source shapes again
548
+ if (excludedInputRoots.some(root => file === root || file.startsWith(root + path.sep))) {
549
+ continue;
550
+ }
551
+
552
+ // Detect relative patterns to preserve directory structure in the shape
553
+ // identifiers (e.g. "nested/leaf.svg" -> "nested--leaf")
554
+ const isRelative = !path.isAbsolute(filePattern) && !filePattern.startsWith('../');
504
555
  const stat = fs.lstatSync(file);
505
556
  let basename;
506
557
 
507
558
  if (stat.isSymbolicLink()) {
508
559
  basename = path.basename(fs.readlinkSync(file));
560
+ } else if (baseDepth > 0) {
561
+ // Strip the base directory marked by "/./" from the shape identifier
562
+ basename = filePattern.split('/').filter(Boolean).slice(baseDepth).join('/');
509
563
  } else {
510
564
  basename = isRelative ? filePattern : path.basename(file);
511
565
  }
package/package.json CHANGED
@@ -1,10 +1,7 @@
1
1
  {
2
2
  "name": "@svgforge/svgforge-cli",
3
- "version": "2.0.0",
3
+ "version": "2.0.2-unstable.202609132246.c961c8e",
4
4
  "author": "Felix Müller (https://github.com/joeda1)",
5
- "contributors": [
6
- "Joschi Kuphal <joschi@kuphal.net> (https://github.com/jkphl)"
7
- ],
8
5
  "description": "Command line interface for svgforge — create SVG sprites & stacks with stylesheet resources (CSS, Sass, etc.)",
9
6
  "homepage": "https://github.com/svgforge/svgforge-cli",
10
7
  "repository": {
@@ -33,20 +30,22 @@
33
30
  "files": [
34
31
  "bin"
35
32
  ],
33
+ "scripts": {
34
+ "prepare": "husky",
35
+ "lint": "xo",
36
+ "fix": "xo --fix",
37
+ "test": "node --test test/*.test.js",
38
+ "test:coverage": "mkdir -p coverage && node --test --experimental-test-coverage --test-reporter=spec --test-reporter-destination=stdout --test-reporter=lcov --test-reporter-destination=coverage/lcov.info"
39
+ },
36
40
  "dependencies": {
41
+ "@svgforge/svgforge": "^2.0.2-unstable.0",
37
42
  "js-yaml": "^5.4.1",
38
- "@svgforge/svgforge": "^2.0.0",
39
43
  "vinyl": "^3.0.1",
40
44
  "yargs": "^18.1.0"
41
45
  },
42
46
  "devDependencies": {
43
47
  "eslint-plugin-jsdoc": "^64.3.2",
48
+ "husky": "^9.1.7",
44
49
  "xo": "^4.0.0"
45
- },
46
- "scripts": {
47
- "lint": "xo",
48
- "fix": "xo --fix",
49
- "test": "node --test test/*.test.js",
50
- "test:coverage": "mkdir -p coverage && node --test --experimental-test-coverage --test-reporter=spec --test-reporter-destination=stdout --test-reporter=lcov --test-reporter-destination=coverage/lcov.info"
51
50
  }
52
- }
51
+ }