@advantacode/brander 0.1.4 → 0.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.
package/README.md CHANGED
@@ -21,6 +21,9 @@ npx --package @advantacode/brander advantacode-brander setup --out src/brander -
21
21
 
22
22
  This creates `brand.config.ts`, adds a `brand:generate` script, patches your stylesheet imports, and prepares the token output folder.
23
23
 
24
+ During setup, Brander creates `brand.css` next to your main stylesheet, writes token/theme imports there, and adds a single `@import './brand.css';` to your main stylesheet.
25
+ The generated `brand:generate` script uses `advantacode-brander generate` and includes any setup generation flags (`--out`, `--format`, `--theme`, `--prefix`) so repeat runs keep writing to the same target.
26
+
24
27
  AdvantaCode Brander generates design tokens and framework adapters from a single brand configuration file. It allows applications, design systems, and design tools to share a consistent source of truth for colors and semantic tokens.
25
28
 
26
29
  For architecture, development, testing, and publishing workflows, see [docs/TECH_OVERVIEW.md](docs/TECH_OVERVIEW.md).
@@ -89,7 +92,7 @@ Supported flags:
89
92
 
90
93
  Setup commands:
91
94
 
92
- * `advantacode-brander setup` configures an existing app by creating `brand.config.ts` if needed, adding a `brand:generate` script, patching a stylesheet with token imports, and generating tokens
95
+ * `advantacode-brander setup` configures an existing app by creating `brand.config.ts` if needed, adding a `brand:generate` script, creating or updating `brand.css` with token imports, patching a stylesheet to import `brand.css`, and generating tokens
93
96
  * `advantacode-brander init` runs the same setup flow for a freshly created app and is intended to be called by a higher-level scaffolder such as `advantacode-init`
94
97
 
95
98
  ## Configuration
@@ -77,6 +77,10 @@ async function loadBrandConfig() {
77
77
  return {};
78
78
  }
79
79
  try {
80
+ // enable TS config loading if needed
81
+ if (configPath.endsWith(".ts") || configPath.endsWith(".mts") || configPath.endsWith(".cts")) {
82
+ await import("tsx/esm");
83
+ }
80
84
  const importedConfig = await import(pathToFileURL(configPath).href);
81
85
  return parseBrandConfig(importedConfig.default ?? importedConfig, configPath);
82
86
  }
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ export async function runCli(args) {
8
8
  return 0;
9
9
  }
10
10
  const command = resolveCommand(args);
11
- const commandArgs = command === 'generate' ? args : args.slice(1);
11
+ const commandArgs = command === "generate" && args[0] === "generate" ? args.slice(1) : command === "generate" ? args : args.slice(1);
12
12
  if (commandArgs.includes('--help') || commandArgs.includes('-h')) {
13
13
  console.log(getHelpText(command));
14
14
  return 0;
@@ -36,6 +36,9 @@ function resolveCommand(args) {
36
36
  if (!firstArg || firstArg.startsWith("-")) {
37
37
  return "generate";
38
38
  }
39
+ if (firstArg === "generate") {
40
+ return "generate";
41
+ }
39
42
  if (firstArg === "setup" || firstArg === "init") {
40
43
  return firstArg;
41
44
  }
@@ -139,55 +142,55 @@ function normalizeFormat(value) {
139
142
  }
140
143
  function getHelpText(command) {
141
144
  if (command === "setup" || command === "init") {
142
- return `AdvantaCode Brander
143
-
144
- Usage:
145
- advantacode-brander ${command} [options]
146
-
147
- Setup options:
148
- --out <dir> Output directory (default: src/brander)
149
- --style <path> Stylesheet file to patch with token imports
150
- --script-name <name> package.json script to create (default: brand:generate)
151
- --skip-imports Do not patch a stylesheet with token imports
152
- --skip-script Do not add a package.json script
153
- --skip-config Do not create brand.config.ts when missing
154
- --skip-generate Do not run token generation after setup
155
-
156
- Generation options:
157
- --format <list> Comma-separated formats: all, css, json, typescript|ts, scss, tailwind, bootstrap, figma
158
- --theme <value> Theme CSS output: light, dark, or both (default: both)
159
- --prefix <value> CSS variable prefix. Use "" or omit for no prefix
160
-
161
- Examples:
162
- advantacode-brander ${command}
163
- advantacode-brander ${command} --out src/brander
164
- advantacode-brander ${command} --style src/style.css
165
- advantacode-brander ${command} --skip-imports --skip-generate
145
+ return `AdvantaCode Brander
146
+
147
+ Usage:
148
+ advantacode-brander ${command} [options]
149
+
150
+ Setup options:
151
+ --out <dir> Output directory (default: src/brander)
152
+ --style <path> Main stylesheet file to patch with a brand.css import
153
+ --script-name <name> package.json script to create (default: brand:generate)
154
+ --skip-imports Do not create/update brand.css or patch stylesheet imports
155
+ --skip-script Do not add a package.json script
156
+ --skip-config Do not create brand.config.js when missing
157
+ --skip-generate Do not run token generation after setup
158
+
159
+ Generation options:
160
+ --format <list> Comma-separated formats: all, css, json, typescript|ts, scss, tailwind, bootstrap, figma
161
+ --theme <value> Theme CSS output: light, dark, or both (default: both)
162
+ --prefix <value> CSS variable prefix. Use "" or omit for no prefix
163
+
164
+ Examples:
165
+ advantacode-brander ${command}
166
+ advantacode-brander ${command} --out src/brander
167
+ advantacode-brander ${command} --style src/style.css
168
+ advantacode-brander ${command} --skip-imports --skip-generate
166
169
  `;
167
170
  }
168
- return `AdvantaCode Brander
169
-
170
- Usage:
171
- advantacode-brander [options]
172
- advantacode-brander setup [options]
173
- advantacode-brander init [options]
174
-
175
- Commands:
176
- setup Configure an existing app to use Brander
177
- init Initialize a new app for Brander-driven tokens
178
-
179
- Options:
180
- -h, --help Show this help output
181
- -v, --version Show the installed package version
182
- --out <dir> Output directory (default: dist/brander)
183
- --format <list> Comma-separated formats: all, css, json, typescript|ts, scss, tailwind, bootstrap, figma
184
- --theme <value> Theme CSS output: light, dark, or both (default: both)
185
- --prefix <value> CSS variable prefix. Use "" or omit for no prefix
186
-
187
- Examples:
188
- advantacode-brander
189
- advantacode-brander --out src/tokens
190
- advantacode-brander setup --out src/brander --style src/style.css
191
- advantacode-brander init --out resources/brander --skip-imports
171
+ return `AdvantaCode Brander
172
+
173
+ Usage:
174
+ advantacode-brander [options]
175
+ advantacode-brander setup [options]
176
+ advantacode-brander init [options]
177
+
178
+ Commands:
179
+ setup Configure an existing app to use Brander
180
+ init Initialize a new app for Brander-driven tokens
181
+
182
+ Options:
183
+ -h, --help Show this help output
184
+ -v, --version Show the installed package version
185
+ --out <dir> Output directory (default: dist/brander)
186
+ --format <list> Comma-separated formats: all, css, json, typescript|ts, scss, tailwind, bootstrap, figma
187
+ --theme <value> Theme CSS output: light, dark, or both (default: both)
188
+ --prefix <value> CSS variable prefix. Use "" or omit for no prefix
189
+
190
+ Examples:
191
+ advantacode-brander
192
+ advantacode-brander --out src/tokens
193
+ advantacode-brander setup --out src/brander --style src/style.css
194
+ advantacode-brander init --out resources/brander --skip-imports
192
195
  `;
193
196
  }
package/dist/setup.js CHANGED
@@ -2,6 +2,7 @@ import fs from "fs";
2
2
  import path from "path";
3
3
  import { generateTokens } from "./generate-tokens.js";
4
4
  const defaultSetupOutputDir = path.join("src", "brander");
5
+ const brandStylesheetFileName = "brand.css";
5
6
  const defaultStyleCandidates = [
6
7
  path.join("src", "style.css"),
7
8
  path.join("src", "main.css"),
@@ -43,12 +44,12 @@ export async function setupProject(options) {
43
44
  }
44
45
  }
45
46
  function ensureBrandConfig() {
46
- const configPath = path.resolve(process.cwd(), "brand.config.ts");
47
+ const configPath = path.resolve(process.cwd(), "brand.config.js");
47
48
  if (fs.existsSync(configPath)) {
48
- return { message: "Kept existing brand.config.ts." };
49
+ return { message: "Kept existing brand.config.js." };
49
50
  }
50
51
  fs.writeFileSync(configPath, getDefaultBrandConfigTemplate());
51
- return { message: "Created brand.config.ts." };
52
+ return { message: "Created brand.config.js." };
52
53
  }
53
54
  function ensurePackageScript(scriptName, command) {
54
55
  const packageJsonPath = path.resolve(process.cwd(), "package.json");
@@ -77,19 +78,49 @@ function ensureStyleImports(stylePath, outputDir) {
77
78
  message: "Skipped stylesheet imports because no stylesheet was found. Use --style <path> to target a file explicitly."
78
79
  };
79
80
  }
81
+ const brandStylesheetPath = path.join(path.dirname(resolvedStylePath), brandStylesheetFileName);
82
+ const brandStylesheetImports = [
83
+ buildImportLine(brandStylesheetPath, path.join(outputDir, "tokens.css")),
84
+ buildImportLine(brandStylesheetPath, path.join(outputDir, "themes", "light.css")),
85
+ buildImportLine(brandStylesheetPath, path.join(outputDir, "themes", "dark.css"))
86
+ ];
87
+ const brandStylesheetContents = `${brandStylesheetImports.join("\n")}\n`;
88
+ const hasBrandStylesheet = fs.existsSync(brandStylesheetPath);
89
+ const existingBrandStylesheet = hasBrandStylesheet ? fs.readFileSync(brandStylesheetPath, "utf8") : "";
90
+ if (existingBrandStylesheet !== brandStylesheetContents) {
91
+ fs.writeFileSync(brandStylesheetPath, brandStylesheetContents);
92
+ }
80
93
  const styleFileContents = fs.readFileSync(resolvedStylePath, "utf8");
81
- const importLines = [
94
+ const legacyTokenImports = [
82
95
  buildImportLine(resolvedStylePath, path.join(outputDir, "tokens.css")),
83
96
  buildImportLine(resolvedStylePath, path.join(outputDir, "themes", "light.css")),
84
97
  buildImportLine(resolvedStylePath, path.join(outputDir, "themes", "dark.css"))
85
98
  ];
86
- const missingImports = importLines.filter((importLine) => !styleFileContents.includes(importLine));
87
- if (missingImports.length === 0) {
88
- return { message: `Kept existing token imports in ${path.relative(process.cwd(), resolvedStylePath)}.` };
89
- }
90
- const nextContents = `${missingImports.join("\n")}\n${styleFileContents}`;
91
- fs.writeFileSync(resolvedStylePath, nextContents);
92
- return { message: `Added token imports to ${path.relative(process.cwd(), resolvedStylePath)}.` };
99
+ const brandImportLine = buildImportLine(resolvedStylePath, brandStylesheetPath);
100
+ const styleLineEnding = styleFileContents.includes("\r\n") ? "\r\n" : "\n";
101
+ const styleLines = styleFileContents.split(/\r?\n/);
102
+ const legacyImportCandidates = new Set();
103
+ for (const importLine of legacyTokenImports) {
104
+ legacyImportCandidates.add(importLine);
105
+ legacyImportCandidates.add(importLine.replace(/'/g, '"'));
106
+ }
107
+ let nextStyleLines = styleLines.filter((line) => !legacyImportCandidates.has(line.trim()));
108
+ const hasBrandImport = nextStyleLines.some((line) => line.trim() === brandImportLine || line.trim() === brandImportLine.replace(/'/g, '"'));
109
+ if (!hasBrandImport) {
110
+ nextStyleLines = [brandImportLine, ...nextStyleLines];
111
+ }
112
+ while (nextStyleLines[0] === "") {
113
+ nextStyleLines = nextStyleLines.slice(1);
114
+ }
115
+ const nextStyleContents = `${nextStyleLines.join(styleLineEnding)}${styleLineEnding}`;
116
+ if (nextStyleContents !== styleFileContents) {
117
+ fs.writeFileSync(resolvedStylePath, nextStyleContents);
118
+ }
119
+ const brandStylesheetStatus = hasBrandStylesheet ? "Updated" : "Created";
120
+ const mainStylesheetStatus = nextStyleContents === styleFileContents ? "Kept" : "Updated";
121
+ return {
122
+ message: `${brandStylesheetStatus} ${path.relative(process.cwd(), brandStylesheetPath)} and ${mainStylesheetStatus.toLowerCase()} ${path.relative(process.cwd(), resolvedStylePath)} to import it.`
123
+ };
93
124
  }
94
125
  function resolveStylePath(stylePath) {
95
126
  if (stylePath) {
@@ -118,7 +149,7 @@ function buildImportLine(stylePath, targetPath) {
118
149
  return `@import '${normalizedImportPath}';`;
119
150
  }
120
151
  function buildGenerateCommand(options) {
121
- const commandParts = ["advantacode-brander"];
152
+ const commandParts = ["advantacode-brander", "generate"];
122
153
  const outputDir = options.outputDir ?? defaultSetupOutputDir;
123
154
  commandParts.push("--out", outputDir);
124
155
  if (options.formats && options.formats.length > 0) {
@@ -133,21 +164,21 @@ function buildGenerateCommand(options) {
133
164
  return commandParts.join(" ");
134
165
  }
135
166
  function getDefaultBrandConfigTemplate() {
136
- return `export default {
137
- name: process.env.COMPANY_NAME || "My Company",
138
- css: {
139
- prefix: process.env.CSS_PREFIX ?? ""
140
- },
141
- colors: {
142
- primary: process.env.PRIMARY_COLOR || "amber-500",
143
- secondary: process.env.SECONDARY_COLOR || "zinc-700",
144
- neutral: process.env.NEUTRAL_COLOR || process.env.SECONDARY_COLOR || "zinc-700",
145
- accent: process.env.ACCENT_COLOR || "amber-400",
146
- info: process.env.INFO_COLOR || "sky-500",
147
- success: process.env.SUCCESS_COLOR || "green-500",
148
- warning: process.env.WARNING_COLOR || "yellow-500",
149
- danger: process.env.DANGER_COLOR || "red-500"
150
- }
151
- };
167
+ return `export default {
168
+ name: process.env.COMPANY_NAME || "My Company",
169
+ css: {
170
+ prefix: process.env.CSS_PREFIX ?? ""
171
+ },
172
+ colors: {
173
+ primary: process.env.PRIMARY_COLOR || "amber-500",
174
+ secondary: process.env.SECONDARY_COLOR || "zinc-700",
175
+ neutral: process.env.NEUTRAL_COLOR || process.env.SECONDARY_COLOR || "zinc-700",
176
+ accent: process.env.ACCENT_COLOR || "amber-400",
177
+ info: process.env.INFO_COLOR || "sky-500",
178
+ success: process.env.SUCCESS_COLOR || "green-500",
179
+ warning: process.env.WARNING_COLOR || "yellow-500",
180
+ danger: process.env.DANGER_COLOR || "red-500"
181
+ }
182
+ };
152
183
  `;
153
184
  }
package/package.json CHANGED
@@ -1,60 +1,60 @@
1
- {
2
- "name": "@advantacode/brander",
3
- "version": "0.1.4",
4
- "description": "AdvantaCode Design System Brand Generator",
5
- "type": "module",
6
- "files": [
7
- "dist/**/*.js",
8
- "docs/*.md",
9
- "README.md",
10
- "LICENSE"
11
- ],
12
- "bin": {
13
- "advantacode-brander": "./dist/cli-wrapper.js"
14
- },
15
- "scripts": {
16
- "clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\"",
17
- "build": "npm run clean && tsc && chmod +x dist/cli-wrapper.js",
18
- "cli": "node --import tsx/esm dist/cli-wrapper.js",
19
- "lint": "eslint --max-warnings=0 src test",
20
- "pretest": "npm run build",
21
- "test": "node --import tsx/esm --test",
22
- "release:check": "npm run lint && npm test && npm pack --dry-run",
23
- "tokens": "tsx src/generate-tokens.ts",
24
- "prepack": "npm run build",
25
- "brand:generate": "advantacode-brander --out src/brander",
26
- "brand:test": "node ./dist/cli-wrapper.js --out src/brander"
27
- },
28
- "license": "MIT",
29
- "author": "Anthony Penn",
30
- "repository": {
31
- "type": "git",
32
- "url": "git+https://github.com/advantacode/advantacode-brander.git"
33
- },
34
- "homepage": "https://github.com/advantacode/advantacode-brander#readme",
35
- "bugs": {
36
- "url": "https://github.com/advantacode/advantacode-brander/issues"
37
- },
38
- "keywords": [
39
- "design-tokens",
40
- "branding",
41
- "oklch",
42
- "tailwind",
43
- "cli"
44
- ],
45
- "engines": {
46
- "node": ">=20.0.0"
47
- },
48
- "devDependencies": {
49
- "@types/node": "^25.3.3",
50
- "@typescript-eslint/eslint-plugin": "^8.56.1",
51
- "@typescript-eslint/parser": "^8.56.1",
52
- "eslint": "^8.0.0",
53
- "typescript": "^5.3.0"
54
- },
55
- "dependencies": {
56
- "culori": "^4.0.2",
57
- "dotenv": "^17.3.1",
58
- "tsx": "^4.0.0"
59
- }
60
- }
1
+ {
2
+ "name": "@advantacode/brander",
3
+ "version": "0.2.0",
4
+ "description": "AdvantaCode Design System Brand Generator",
5
+ "type": "module",
6
+ "files": [
7
+ "dist/**/*.js",
8
+ "docs/*.md",
9
+ "README.md",
10
+ "LICENSE"
11
+ ],
12
+ "bin": {
13
+ "advantacode-brander": "./dist/cli-wrapper.js"
14
+ },
15
+ "scripts": {
16
+ "clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\"",
17
+ "build": "npm run clean && tsc && chmod +x dist/cli-wrapper.js",
18
+ "cli": "node --import tsx/esm dist/cli-wrapper.js",
19
+ "lint": "eslint --max-warnings=0 src test",
20
+ "pretest": "npm run build",
21
+ "test": "node --import tsx/esm --test",
22
+ "release:check": "npm run lint && npm test && npm pack --dry-run",
23
+ "tokens": "tsx src/generate-tokens.ts",
24
+ "prepack": "npm run build",
25
+ "brand:generate": "advantacode-brander --out src/brander",
26
+ "brand:test": "node ./dist/cli-wrapper.js --out src/brander"
27
+ },
28
+ "license": "MIT",
29
+ "author": "Anthony Penn",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/advantacode/advantacode-brander.git"
33
+ },
34
+ "homepage": "https://github.com/advantacode/advantacode-brander#readme",
35
+ "bugs": {
36
+ "url": "https://github.com/advantacode/advantacode-brander/issues"
37
+ },
38
+ "keywords": [
39
+ "design-tokens",
40
+ "branding",
41
+ "oklch",
42
+ "tailwind",
43
+ "cli"
44
+ ],
45
+ "engines": {
46
+ "node": ">=20.0.0"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^25.3.3",
50
+ "@typescript-eslint/eslint-plugin": "^8.56.1",
51
+ "@typescript-eslint/parser": "^8.56.1",
52
+ "eslint": "^8.0.0",
53
+ "typescript": "^5.3.0"
54
+ },
55
+ "dependencies": {
56
+ "culori": "^4.0.2",
57
+ "dotenv": "^17.3.1",
58
+ "tsx": "^4.21.0"
59
+ }
60
+ }