@multicomma/arkcn 0.1.0 → 0.1.1

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/cli/commands.mjs CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  CONFIG_FILE,
5
5
  defaultConfig,
6
6
  detectBundlerAlias,
7
+ ensureViteConfig,
7
8
  detectPackageManager,
8
9
  ensureTsconfigAlias,
9
10
  installDependencies,
@@ -96,10 +97,15 @@ export async function init(cwd, registry, flags) {
96
97
  else if (alias === "ok") ok(`tsconfig already maps "${config.alias}/*"`)
97
98
  else warn(`Add "paths": { "${config.alias}/*": ["./${config.srcDir}/*"] } to your tsconfig compilerOptions.`)
98
99
  const bundler = detectBundlerAlias(cwd, config.alias)
99
- if (bundler.framework === "vite" && !bundler.ok)
100
- warn(
101
- `Add the alias to ${bundler.file}: resolve.alias { "${config.alias}": path.resolve(__dirname, "./${config.srcDir}") }`
102
- )
100
+ if (bundler.framework === "vite") {
101
+ const vite = ensureViteConfig(cwd, config.alias, config.srcDir)
102
+ if (vite.added.length) ok(`Added ${vite.added.join(" and ")} to ${vite.file}`)
103
+ else if (vite.manual)
104
+ warn(
105
+ `Add to ${vite.file}: the @tailwindcss/vite plugin and resolve.alias { "${config.alias}": "./${config.srcDir}" }`
106
+ )
107
+ else ok(`${vite.file} already has the alias and the Tailwind plugin`)
108
+ }
103
109
 
104
110
  const { items } = await registry.closure(["utils"])
105
111
  for (const item of items) writeItem(cwd, config, item, { overwrite: false })
@@ -118,6 +124,7 @@ export async function init(cwd, registry, flags) {
118
124
  const deps = Object.entries(index.baseDependencies)
119
125
  .filter(([dep]) => !installedVersion(cwd, dep))
120
126
  .map(([dep, version]) => `${dep}@${version}`)
127
+ if (bundler.framework === "vite" && !installedVersion(cwd, "@tailwindcss/vite")) deps.push("@tailwindcss/vite")
121
128
  await install(cwd, deps, flags)
122
129
  log(`\nNext: ${c.cyan("npx @multicomma/arkcn add button card dialog")}`)
123
130
  }
package/cli/project.mjs CHANGED
@@ -120,33 +120,79 @@ export function installDependencies(cwd, deps) {
120
120
  return { ok: result.status === 0, pm, args }
121
121
  }
122
122
 
123
- /** Ensure tsconfig paths has "<alias>/*": ["./<srcDir>/*"]. Returns "ok" | "added" | "manual". */
123
+ /**
124
+ * Ensure tsconfig paths has "<alias>/*": ["./<srcDir>/*"]. Works on JSON with comments (Vite's
125
+ * templates) by inserting text instead of re-serialising. Returns "ok" | "added" | "manual".
126
+ */
124
127
  export function ensureTsconfigAlias(cwd, alias, srcDir = "src") {
125
128
  for (const name of ["tsconfig.app.json", "tsconfig.json"]) {
126
129
  const file = join(cwd, name)
127
130
  if (!existsSync(file)) continue
128
131
  const raw = readFileSync(file, "utf8")
129
- let json
130
- try {
131
- json = JSON.parse(
132
- raw
133
- .replace(/\/\*[\s\S]*?\*\//g, "")
134
- .replace(/^\s*\/\/.*$/gm, "")
135
- .replace(/,(\s*[}\]])/g, "$1")
136
- )
137
- } catch {
138
- return "manual"
132
+ const key = `"${alias}/*"`
133
+ if (raw.includes(key)) return "ok"
134
+ const entry = `${key}: ["./${srcDir}/*"]`
135
+ const pathsMatch = raw.match(/"paths"\s*:\s*\{/)
136
+ if (pathsMatch) {
137
+ const at = pathsMatch.index + pathsMatch[0].length
138
+ const rest = raw.slice(at)
139
+ const empty = /^\s*\}/.test(rest)
140
+ writeFileSync(file, raw.slice(0, at) + `\n ${entry}${empty ? "" : ","}` + rest)
141
+ return "added"
139
142
  }
140
- const paths = json.compilerOptions?.paths ?? {}
141
- if (paths[`${alias}/*`]) return "ok"
142
- if (raw.includes("//") || raw.includes("/*")) return "manual"
143
- json.compilerOptions = { ...json.compilerOptions, paths: { ...paths, [`${alias}/*`]: [`./${srcDir}/*`] } }
144
- writeFileSync(file, JSON.stringify(json, null, 2) + "\n")
143
+ const optionsMatch = raw.match(/"compilerOptions"\s*:\s*\{/)
144
+ if (!optionsMatch) continue
145
+ const at = optionsMatch.index + optionsMatch[0].length
146
+ const rest = raw.slice(at)
147
+ const empty = /^\s*\}/.test(rest)
148
+ writeFileSync(file, raw.slice(0, at) + `\n "paths": {\n ${entry}\n }${empty ? "" : ","}` + rest)
145
149
  return "added"
146
150
  }
147
151
  return "manual"
148
152
  }
149
153
 
154
+ /**
155
+ * Ensure a Vite config has the "@" alias and the Tailwind plugin. Returns what was added.
156
+ * Next.js reads tsconfig paths on its own, so it only needs the tsconfig step.
157
+ */
158
+ export function ensureViteConfig(cwd, alias, srcDir = "src") {
159
+ const name = ["vite.config.ts", "vite.config.mts", "vite.config.js", "vite.config.mjs"].find((n) =>
160
+ existsSync(join(cwd, n))
161
+ )
162
+ if (!name) return { file: null, added: [] }
163
+ const file = join(cwd, name)
164
+ let raw = readFileSync(file, "utf8")
165
+ const added = []
166
+ const hasAlias = raw.includes(`"${alias}"`) || raw.includes(`'${alias}'`) || raw.includes("tsconfigPaths")
167
+ const hasTailwind = raw.includes("@tailwindcss/vite")
168
+ const config = raw.match(/defineConfig\(\s*\{/)
169
+ if (!config) return { file: name, added, manual: !hasAlias || !hasTailwind }
170
+ if (!hasTailwind) {
171
+ raw = `import tailwindcss from "@tailwindcss/vite"\n` + raw
172
+ const plugins = raw.match(/plugins\s*:\s*\[/)
173
+ if (plugins) {
174
+ const at = plugins.index + plugins[0].length
175
+ raw = raw.slice(0, at) + "tailwindcss(), " + raw.slice(at)
176
+ } else {
177
+ const m = raw.match(/defineConfig\(\s*\{/)
178
+ raw = raw.slice(0, m.index + m[0].length) + "\n plugins: [tailwindcss()]," + raw.slice(m.index + m[0].length)
179
+ }
180
+ added.push("tailwind plugin")
181
+ }
182
+ if (!hasAlias) {
183
+ if (!/from ["']node:path["']|from ["']path["']/.test(raw)) raw = `import path from "node:path"\n` + raw
184
+ const m = raw.match(/defineConfig\(\s*\{/)
185
+ const at = m.index + m[0].length
186
+ raw =
187
+ raw.slice(0, at) +
188
+ `\n resolve: {\n alias: { "${alias}": path.resolve(import.meta.dirname, "./${srcDir}") },\n },` +
189
+ raw.slice(at)
190
+ added.push("alias")
191
+ }
192
+ if (added.length) writeFileSync(file, raw)
193
+ return { file: name, added }
194
+ }
195
+
150
196
  export function detectBundlerAlias(cwd, alias) {
151
197
  const candidates = [
152
198
  "vite.config.ts",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multicomma/arkcn",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "arkcn: shadcn/ui-style components ported to Ark UI, plus data-heavy primitives (data grid, kanban, gantt, scheduler, node graph, ...). Tailwind v4. Ships a copy-into-your-project CLI and an MCP server.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@multicomma/arkcn",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "baseDependencies": {
5
5
  "react": "^19.2.8",
6
6
  "react-dom": "^19.2.8",