tailjng 0.0.62 → 0.1.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 (54) hide show
  1. package/README.md +186 -32
  2. package/cli/component-manager.js +71 -20
  3. package/cli/execute/init-app.js +251 -0
  4. package/cli/file-operations.js +45 -29
  5. package/cli/index.js +66 -15
  6. package/cli/settings/components-list.js +4 -151
  7. package/cli/settings/header-generator.js +9 -10
  8. package/cli/settings/lib-utils.js +89 -0
  9. package/cli/settings/overwrite-policy.js +18 -0
  10. package/cli/settings/path-utils.js +14 -29
  11. package/cli/settings/project-utils.js +220 -0
  12. package/cli/settings/prompt-utils.js +66 -5
  13. package/cli/templates/app.generator.js +382 -0
  14. package/fesm2022/tailjng.mjs +232 -66
  15. package/fesm2022/tailjng.mjs.map +1 -1
  16. package/lib/services/static/colors.service.d.ts +17 -0
  17. package/lib/services/transformer/transform.service.d.ts +3 -3
  18. package/package.json +1 -1
  19. package/public-api.d.ts +2 -0
  20. package/registry/components.json +164 -0
  21. package/src/lib/components/alert/alert-dialog/dialog-alert.component.css +17 -0
  22. package/src/lib/components/alert/alert-dialog/dialog-alert.component.html +83 -51
  23. package/src/lib/components/alert/alert-dialog/dialog-alert.component.ts +85 -53
  24. package/src/lib/components/alert/alert-toast/toast-alert.component.css +38 -4
  25. package/src/lib/components/alert/alert-toast/toast-alert.component.html +72 -40
  26. package/src/lib/components/alert/alert-toast/toast-alert.component.ts +84 -19
  27. package/src/lib/components/badge/badge.component.ts +1 -2
  28. package/src/lib/components/button/button.component.css +14 -0
  29. package/src/lib/components/button/button.component.html +17 -17
  30. package/src/lib/components/button/button.component.ts +139 -48
  31. package/src/lib/components/card/card-crud-complete/complete-crud-card.component.ts +5 -1
  32. package/src/lib/components/checkbox/checkbox-switch/switch-checkbox.component.html +1 -1
  33. package/src/lib/components/filter/filter-complete/complete-filter.component.html +1 -1
  34. package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.html +8 -5
  35. package/src/lib/components/menu/options-coach-menu/options-coach-menu.component.scss +12 -0
  36. package/src/lib/components/select/select-dropdown/dropdown-select.component.css +4 -0
  37. package/src/lib/components/select/select-dropdown/dropdown-select.component.html +1 -1
  38. package/src/lib/components/select/select-dropdown/dropdown-select.component.ts +3 -3
  39. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.css +4 -0
  40. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.html +1 -1
  41. package/src/lib/components/select/select-multi-dropdown/multi-dropdown-select.component.ts +30 -20
  42. package/src/lib/components/table/table-crud-complete/complete-crud-table.component.html +504 -170
  43. package/src/lib/components/table/table-crud-complete/complete-crud-table.component.scss +92 -0
  44. package/src/lib/components/table/table-crud-complete/complete-crud-table.component.ts +139 -5
  45. package/src/lib/components/table/table-crud-complete/expand-grid/table-expand-grid.builder.ts +116 -0
  46. package/src/lib/components/table/table-crud-complete/expand-grid/table-expand-grid.helper.ts +43 -0
  47. package/src/lib/components/table/table-crud-complete/expand-grid/table-expand-grid.types.ts +39 -0
  48. package/src/lib/components/table/table-crud-complete/index.ts +3 -0
  49. package/src/lib/components/toggle-radio/toggle-radio.component.css +4 -0
  50. package/src/lib/components/toggle-radio/toggle-radio.component.html +4 -4
  51. package/src/lib/components/toggle-radio/toggle-radio.component.ts +15 -6
  52. package/src/lib/components/tooltip/tooltip.service.ts +0 -30
  53. package/tailjng-0.1.0.tgz +0 -0
  54. package/src/lib/components/color/colors.service.ts +0 -187
@@ -7,10 +7,50 @@ const { generateHeaderComment } = require("./settings/header-generator")
7
7
  const { askOverwrite } = require("./settings/prompt-utils")
8
8
  const { buildTargetPath, parseComponentPath } = require("./settings/path-utils")
9
9
 
10
+ const TEXT_EXTENSIONS = new Set([".ts", ".js", ".html", ".css", ".scss", ".json", ".md"])
11
+
12
+ function shouldAddHeader(fileName) {
13
+ return TEXT_EXTENSIONS.has(path.extname(fileName).toLowerCase())
14
+ }
15
+
16
+ function copyFileWithHeader(srcFile, destFile) {
17
+ fs.mkdirSync(path.dirname(destFile), { recursive: true })
18
+
19
+ if (!shouldAddHeader(path.basename(srcFile))) {
20
+ fs.copyFileSync(srcFile, destFile)
21
+ return
22
+ }
23
+
24
+ let content = fs.readFileSync(srcFile, "utf8")
25
+ const headerComment = generateHeaderComment(path.basename(srcFile))
26
+ content = headerComment + "\n\n" + content
27
+ fs.writeFileSync(destFile, content)
28
+ }
29
+
30
+ function copyDirectoryRecursive(srcDir, destDir, projectRoot) {
31
+ const entries = fs.readdirSync(srcDir, { withFileTypes: true })
32
+
33
+ for (const entry of entries) {
34
+ const srcPath = path.join(srcDir, entry.name)
35
+ const destPath = path.join(destDir, entry.name)
36
+
37
+ if (entry.isDirectory()) {
38
+ copyDirectoryRecursive(srcPath, destPath, projectRoot)
39
+ continue
40
+ }
41
+
42
+ if (!entry.isFile()) continue
43
+
44
+ copyFileWithHeader(srcPath, destPath)
45
+ const relativePath = path.relative(projectRoot, destPath)
46
+ console.log(
47
+ `${COLORS.green}${COLORS.bright}[tailjng CLI]${COLORS.reset} ${COLORS.green}Copied file: ${COLORS.bright}${relativePath}${COLORS.reset}`,
48
+ )
49
+ }
50
+ }
51
+
10
52
  async function copyComponentFiles(componentName, componentPath, isDependency = false) {
11
- // Detect the root directory where node_modules is located
12
53
  let currentDir = process.cwd()
13
- // Up to the root directory where node_modules is located
14
54
  while (!fs.existsSync(path.join(currentDir, "node_modules"))) {
15
55
  currentDir = path.dirname(currentDir)
16
56
  if (currentDir === path.dirname(currentDir)) {
@@ -19,15 +59,11 @@ async function copyComponentFiles(componentName, componentPath, isDependency = f
19
59
  }
20
60
  }
21
61
 
22
- // Now currentDir points to the root directory where node_modules is located
23
62
  const nodeModulesPath = path.join(currentDir, "node_modules", "tailjng", componentPath)
24
63
  const projectRoot = process.cwd()
25
-
26
- // Use the new function to build the target path while maintaining the folder structure
27
64
  const targetPath = buildTargetPath(projectRoot, componentName, componentPath)
28
65
  const pathInfo = parseComponentPath(componentPath)
29
66
 
30
- // Check if the component exists in the correct path
31
67
  if (!fs.existsSync(nodeModulesPath)) {
32
68
  console.error(
33
69
  `${COLORS.red}${COLORS.bright}[tailjng CLI]${COLORS.reset} ${COLORS.red}ERROR: Component path ${COLORS.bright}"${nodeModulesPath}"${COLORS.reset} ${COLORS.red}does not exist.${COLORS.reset}`,
@@ -35,17 +71,15 @@ async function copyComponentFiles(componentName, componentPath, isDependency = f
35
71
  process.exit(1)
36
72
  }
37
73
 
38
- // Check if the target path already exists
39
74
  if (fs.existsSync(targetPath)) {
40
75
  const relativeTargetPath = path.relative(projectRoot, targetPath)
41
76
  const shouldOverwrite = await askOverwrite(componentName, relativeTargetPath, isDependency)
42
77
 
43
78
  if (!shouldOverwrite) {
44
79
  console.log(`${COLORS.dim}${COLORS.bright}[tailjng CLI]${COLORS.reset} ${COLORS.dim}Skipping ${COLORS.bright}"${componentName}"${COLORS.reset} ${COLORS.dim}- keeping existing version.${COLORS.reset}`)
45
- return false // Return false to indicate that it was not installed
80
+ return false
46
81
  }
47
82
 
48
- // If decided to overwrite, remove the existing directory
49
83
  console.log(`${COLORS.yellow}${COLORS.bright}[tailjng CLI]${COLORS.reset} ${COLORS.yellow}Removing existing ${COLORS.bright}"${componentName}"${COLORS.reset} ${COLORS.yellow}to overwrite...${COLORS.reset}`)
50
84
  fs.rmSync(targetPath, { recursive: true, force: true })
51
85
  }
@@ -53,7 +87,6 @@ async function copyComponentFiles(componentName, componentPath, isDependency = f
53
87
  const componentType = isDependency ? "dependency component" : "component"
54
88
  const relativeTargetPath = path.relative(projectRoot, targetPath)
55
89
 
56
- // See if the component has subfolders
57
90
  if (pathInfo.hasSubfolders) {
58
91
  console.log(
59
92
  `${COLORS.blue}${COLORS.bright}[tailjng CLI]${COLORS.reset} ${COLORS.blue}Copying ${componentType} ${COLORS.bright}"${componentName}"${COLORS.reset} (${pathInfo.fullSubPath}) → ${relativeTargetPath}${COLORS.reset}`,
@@ -64,27 +97,10 @@ async function copyComponentFiles(componentName, componentPath, isDependency = f
64
97
  )
65
98
  }
66
99
 
67
- // Create the target directory structure
68
100
  fs.mkdirSync(targetPath, { recursive: true })
101
+ copyDirectoryRecursive(nodeModulesPath, targetPath, projectRoot)
69
102
 
70
- const files = fs.readdirSync(nodeModulesPath)
71
- files.forEach((file) => {
72
- const srcFile = path.join(nodeModulesPath, file)
73
- const destFile = path.join(targetPath, file)
74
- let content = fs.readFileSync(srcFile, "utf8")
75
-
76
- // Add header comment to the content
77
- const headerComment = generateHeaderComment(file)
78
- content = headerComment + "\n\n" + content
79
-
80
- // Here you can add more processing logic if needed
81
- // For example, replace imports, etc.
82
-
83
- fs.writeFileSync(destFile, content)
84
- console.log(`${COLORS.green}${COLORS.bright}[tailjng CLI]${COLORS.reset} ${COLORS.green}Copied file: ${COLORS.bright}${file}${COLORS.reset}`)
85
- })
86
-
87
- return true // Return true to indicate that it was installed successfully
103
+ return true
88
104
  }
89
105
 
90
106
  module.exports = { copyComponentFiles }
package/cli/index.js CHANGED
@@ -1,15 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // index.js
3
+ // index.js — tailjng CLI entry point
4
+
4
5
  const { checkTailwindInstalled } = require("./settings/tailwind-check")
5
6
  const { addComponent, installAllComponents } = require("./component-manager")
6
7
  const { getComponentList } = require("./settings/components-list")
8
+ const { getPackageVersion } = require("./settings/lib-utils")
9
+ const { isComponentInstalled } = require("./settings/path-utils")
7
10
  const { COLORS } = require("./settings/colors")
8
11
 
9
12
  const args = process.argv.slice(2)
10
13
  const command = args[0]
11
14
 
12
15
  async function main() {
16
+ if (command === "init:app") {
17
+ require("./execute/init-app")
18
+ return
19
+ }
20
+
13
21
  if (command === "add") {
14
22
  const componentName = args[1]
15
23
  if (!componentName) {
@@ -19,37 +27,80 @@ async function main() {
19
27
  }
20
28
  checkTailwindInstalled()
21
29
  await addComponent(componentName, getComponentList())
22
- } else if (command === "list") {
30
+ return
31
+ }
32
+
33
+ if (command === "list") {
23
34
  showComponentList()
24
- } else if (command === "install-all") {
35
+ return
36
+ }
37
+
38
+ if (command === "install-all") {
25
39
  checkTailwindInstalled()
26
40
  await installAllComponents(getComponentList())
27
- } else {
28
- console.log(`${COLORS.bright}${COLORS.blue}Usage:${COLORS.reset}
29
- ${COLORS.cyan}${COLORS.bright}npx tailjng${COLORS.reset} ${COLORS.bright}${COLORS.green}add${COLORS.reset} ${COLORS.dim}<componentName>${COLORS.reset} ${COLORS.blue}Add a component to your project${COLORS.reset}
30
- ${COLORS.cyan}${COLORS.bright}npx tailjng${COLORS.reset} ${COLORS.bright}${COLORS.green}list${COLORS.reset} ${COLORS.blue}List all available components${COLORS.reset}
31
- ${COLORS.cyan}${COLORS.bright}npx tailjng${COLORS.reset} ${COLORS.bright}${COLORS.green}install-all${COLORS.reset} ${COLORS.blue}Install all available components${COLORS.reset}`)
41
+ return
42
+ }
43
+
44
+ if (command === "version") {
45
+ console.log(`${COLORS.cyan}tailjng${COLORS.reset} v${getPackageVersion(__dirname)}`)
46
+ return
32
47
  }
48
+
49
+ showHelp()
50
+ }
51
+
52
+ function showHelp() {
53
+ console.log(`${COLORS.bright}${COLORS.blue}tailjng CLI${COLORS.reset} v${getPackageVersion(__dirname)}
54
+
55
+ ${COLORS.bright}Project setup${COLORS.reset}
56
+ ${COLORS.cyan}npx tailjng init:app${COLORS.reset} Configure Tailwind, providers, styles (no UI)
57
+ ${COLORS.cyan}npx tailjng init:app --with-components${COLORS.reset} init:app + mode-toggle and alerts
58
+
59
+ ${COLORS.bright}Components${COLORS.reset} ${COLORS.dim}(run from your Angular app directory)${COLORS.reset}
60
+ ${COLORS.cyan}npx tailjng add${COLORS.reset} ${COLORS.dim}<name>${COLORS.reset} Install one component + dependencies
61
+ ${COLORS.cyan}npx tailjng install-all${COLORS.reset} Install all registry components
62
+ ${COLORS.cyan}npx tailjng list${COLORS.reset} List available components
63
+
64
+ ${COLORS.bright}Info${COLORS.reset}
65
+ ${COLORS.cyan}npx tailjng version${COLORS.reset} Show installed package version`)
33
66
  }
34
67
 
35
68
  function showComponentList() {
36
69
  const componentList = getComponentList()
37
- if (Object.keys(componentList).length === 0) {
70
+ const names = Object.keys(componentList).sort()
71
+ const projectRoot = process.cwd()
72
+
73
+ if (names.length === 0) {
38
74
  console.log(`${COLORS.yellow}[tailjng CLI] No components available.${COLORS.reset}`)
39
75
  return
40
76
  }
41
77
 
42
- console.log(`${COLORS.cyan}[tailjng CLI] Available components:${COLORS.reset}`)
43
- for (const component in componentList) {
44
- const dependencies =
45
- componentList[component].dependencies.length > 0 ? componentList[component].dependencies.join(", ") : "None"
78
+ let installedCount = 0
79
+
80
+ console.log(`${COLORS.cyan}[tailjng CLI] Available components (${names.length}):${COLORS.reset}`)
81
+ console.log(`${COLORS.dim}Use: npx tailjng add <name>${COLORS.reset}\n`)
82
+
83
+ for (const component of names) {
84
+ const meta = componentList[component]
85
+ const deps = meta.dependencies
86
+ const dependencies = deps.length > 0 ? deps.join(", ") : "none"
87
+ const installed = isComponentInstalled(projectRoot, component, meta.path)
88
+ const status = installed
89
+ ? `${COLORS.green}[installed]${COLORS.reset}`
90
+ : `${COLORS.dim}[not installed]${COLORS.reset}`
91
+
92
+ if (installed) installedCount++
93
+
46
94
  console.log(
47
- `${COLORS.green}- ${component}${COLORS.reset} ${COLORS.dim}(Dependencies: ${dependencies})${COLORS.reset}`,
95
+ `${COLORS.green}${component}${COLORS.reset} ${status} ${COLORS.dim}(dependencies: ${dependencies})${COLORS.reset}`,
48
96
  )
49
97
  }
98
+
99
+ console.log(
100
+ `\n${COLORS.dim}${installedCount}/${names.length} components already in this project.${COLORS.reset}`,
101
+ )
50
102
  }
51
103
 
52
- // Execute the main function
53
104
  main().catch((error) => {
54
105
  console.error(`${COLORS.red}[tailjng CLI] Unexpected error:${COLORS.reset}`, error)
55
106
  process.exit(1)
@@ -1,156 +1,9 @@
1
- // components-list.js
2
-
3
- function getComponentList() {
4
- return {
5
- 'mode-toggle': {
6
- path: "src/lib/components/mode-toggle",
7
- dependencies: [],
8
- },
9
- 'color': {
10
- path: "src/lib/components/color",
11
- dependencies: [],
12
- },
13
- 'tooltip': {
14
- path: "src/lib/components/tooltip",
15
- dependencies: [],
16
- },
17
- 'coach-mark': {
18
- path: "src/lib/components/coach-mark",
19
- dependencies: [],
20
- },
21
- 'badge': {
22
- path: "src/lib/components/badge",
23
- dependencies: ["tooltip"],
24
- },
25
- 'label': {
26
- path: "src/lib/components/label",
27
- dependencies: ["tooltip"],
28
- },
29
- 'toggle-radio': {
30
- path: "src/lib/components/toggle-radio",
31
- dependencies: [],
32
- },
33
- 'button': {
34
- path: "src/lib/components/button",
35
- dependencies: ["tooltip", "color"],
36
- },
37
- 'checkbox-input': {
38
- path: "src/lib/components/checkbox/checkbox-input",
39
- dependencies: ["tooltip", "color"],
40
- },
41
- 'checkbox-switch': {
42
- path: "src/lib/components/checkbox/checkbox-switch",
43
- dependencies: ["color"],
44
- },
45
- 'alert-dialog': {
46
- path: "src/lib/components/alert/alert-dialog",
47
- dependencies: ["button", "color"],
48
- },
49
- 'alert-toast': {
50
- path: "src/lib/components/alert/alert-toast",
51
- dependencies: ["button", "color"],
52
- },
53
- 'progress-bar': {
54
- path: "src/lib/components/progress-bar",
55
- dependencies: [],
56
- },
57
- 'viewer-image': {
58
- path: "src/lib/components/viewer/viewer-image",
59
- dependencies: ["button"],
60
- },
61
- 'viewer-pdf': {
62
- path: "src/lib/components/viewer/viewer-pdf",
63
- dependencies: [],
64
- },
65
- 'dialog': {
66
- path: "src/lib/components/dialog",
67
- dependencies: [],
68
- },
69
- 'input-file': {
70
- path: "src/lib/components/input/input-file",
71
- dependencies: [],
72
- },
73
- 'input-textarea': {
74
- path: "src/lib/components/input/input-textarea",
75
- dependencies: [],
76
- },
77
- 'input-range': {
78
- path: "src/lib/components/input/input-range",
79
- dependencies: [],
80
- },
81
- 'input': {
82
- path: "src/lib/components/input/input",
83
- dependencies: [],
84
- },
85
- 'select-multi-table': {
86
- path: "src/lib/components/select/select-multi-table",
87
- dependencies: ["button"],
88
- },
89
- 'select-multi-dropdown': {
90
- path: "src/lib/components/select/select-multi-dropdown",
91
- dependencies: [],
92
- },
93
- 'select-dropdown': {
94
- path: "src/lib/components/select/select-dropdown",
95
- dependencies: [],
96
- },
97
- 'sidebar-static': {
98
- path: "src/lib/components/sidebar/sidebar-static",
99
- dependencies: ["checkbox-switch"],
100
- },
101
- 'form-container': {
102
- path: "src/lib/components/form/form-container",
103
- dependencies: [],
104
- },
105
- 'form-validation': {
106
- path: "src/lib/components/form/form-validation",
107
- dependencies: [],
108
- },
109
- 'form-sidebar': {
110
- path: "src/lib/components/form/form-sidebar",
111
- dependencies: ["tooltip", "button", "checkbox-switch", "coach-mark"],
112
- },
113
- 'paginator-complete': {
114
- path: "src/lib/components/paginator/paginator-complete",
115
- dependencies: ["button"],
116
- },
117
- 'filter-complete': {
118
- path: "src/lib/components/filter/filter-complete",
119
- dependencies: ["select-dropdown", "select-multi-table", "button", "dialog", "checkbox-switch"],
120
- },
121
- 'card-crud-complete': {
122
- path: "src/lib/components/card/card-crud-complete",
123
- dependencies: ["paginator-complete", "filter-complete"],
124
- },
125
- 'card-complete': {
126
- path: "src/lib/components/card/card-complete",
127
- dependencies: ["paginator-complete", "filter-complete"],
128
- },
129
- 'menu-options-table': {
130
- path: "src/lib/components/menu/menu-options-table",
131
- dependencies: ["button"],
132
- },
133
- 'options-coach-menu': {
134
- path: "src/lib/components/menu/options-coach-menu",
135
- dependencies: ["button", "coach-mark"],
136
- },
137
-
1
+ // components-list.js — reads installable components from registry/components.json
138
2
 
3
+ const { loadComponentRegistry } = require("./lib-utils")
139
4
 
140
-
141
- 'table-crud-complete': {
142
- path: "src/lib/components/table/table-crud-complete",
143
- dependencies: ["button", "paginator-complete", "filter-complete", "checkbox-input", "options-coach-menu", "dialog", "viewer-image", "select-dropdown", "input"],
144
- },
145
- 'table-complete': {
146
- path: "src/lib/components/table/table-complete",
147
- dependencies: ["button", "paginator-complete", "filter-complete", "checkbox-input", "options-coach-menu", "dialog", "viewer-image", "select-dropdown", "input"],
148
- },
149
- 'theme-generator': {
150
- path: "src/lib/components/theme-generator",
151
- dependencies: ["input", "input-range", "button", "label", "form-container"],
152
- },
153
- }
5
+ function getComponentList() {
6
+ return loadComponentRegistry(__dirname)
154
7
  }
155
8
 
156
9
  module.exports = { getComponentList }
@@ -1,9 +1,11 @@
1
- // header-generator.js
1
+ // header-generator.js — prepends attribution header when copying components into consumer apps
2
2
 
3
3
  const path = require("path")
4
+ const { getPackageVersion } = require("./lib-utils")
4
5
 
5
6
  function generateHeaderComment(fileName) {
6
- const headerContent = `===============================================
7
+ const version = getPackageVersion(__dirname)
8
+ const headerContent = `===============================================
7
9
  Component and Function Library - tailjNg
8
10
  ===============================================
9
11
  Description:
@@ -27,18 +29,15 @@ Authors:
27
29
  License:
28
30
  This project is licensed under the BSD 3-Clause - see the LICENSE file for more details.
29
31
 
30
- Version: 0.0.62
32
+ Version: ${version}
31
33
  Creation Date: 2025-01-04
32
34
  ===============================================`
33
35
 
34
- const ext = path.extname(fileName).toLowerCase()
35
- if (ext === ".ts" || ext === ".js" || ext === ".css" || ext === ".scss") {
36
+ const ext = path.extname(fileName).toLowerCase()
37
+ if (ext === ".html") {
38
+ return `<!--\n${headerContent}\n-->`
39
+ }
36
40
  return `/*\n${headerContent}\n*/`
37
- } else if (ext === ".html") {
38
- return `<!--\n${headerContent}\n-->`
39
- } else {
40
- return `/*\n${headerContent}\n*/`
41
- }
42
41
  }
43
42
 
44
43
  module.exports = { generateHeaderComment }
@@ -0,0 +1,89 @@
1
+ // lib-utils.js — shared helpers for package version and project config
2
+
3
+ const fs = require("fs")
4
+ const path = require("path")
5
+
6
+ function readJsonFile(filePath) {
7
+ try {
8
+ return JSON.parse(fs.readFileSync(filePath, "utf8"))
9
+ } catch {
10
+ return null
11
+ }
12
+ }
13
+
14
+ /**
15
+ * Resolves tailjng package root from any CLI module location.
16
+ * Works in source (projects/tailjng/cli/...) and published (node_modules/tailjng/cli/...).
17
+ */
18
+ function getPackageRoot(fromDir = __dirname) {
19
+ let dir = fromDir
20
+ for (let i = 0; i < 6; i++) {
21
+ const pkgPath = path.join(dir, "package.json")
22
+ if (fs.existsSync(pkgPath)) {
23
+ const pkg = readJsonFile(pkgPath)
24
+ if (pkg?.name === "tailjng") {
25
+ return dir
26
+ }
27
+ }
28
+ const parent = path.dirname(dir)
29
+ if (parent === dir) break
30
+ dir = parent
31
+ }
32
+ return path.join(fromDir, "..", "..")
33
+ }
34
+
35
+ function getPackageVersion(fromDir = __dirname) {
36
+ const pkgPath = path.join(getPackageRoot(fromDir), "package.json")
37
+ const pkg = readJsonFile(pkgPath)
38
+ return pkg?.version ?? "unknown"
39
+ }
40
+
41
+ function getRegistryPath(fromDir = __dirname) {
42
+ return path.join(getPackageRoot(fromDir), "registry", "components.json")
43
+ }
44
+
45
+ function loadComponentRegistry(fromDir = __dirname) {
46
+ const registryPath = getRegistryPath(fromDir)
47
+ const registry = readJsonFile(registryPath)
48
+ if (!registry?.components) {
49
+ throw new Error(`Component registry not found or invalid: ${registryPath}`)
50
+ }
51
+ return registry.components
52
+ }
53
+
54
+ function getTailjngConfig(projectRoot) {
55
+ return readJsonFile(path.join(projectRoot, ".tailjng", "paths.json"))
56
+ }
57
+
58
+ function getComponentsBasePath(projectRoot) {
59
+ const config = getTailjngConfig(projectRoot)
60
+ const componentsPath = config?.components ?? "src/app/tailjng"
61
+ return path.join(projectRoot, componentsPath)
62
+ }
63
+
64
+ /**
65
+ * Returns all transitive dependencies for a component in install order.
66
+ */
67
+ function collectDependencyTree(componentName, componentList, collected = [], visited = new Set()) {
68
+ const component = componentList[componentName]
69
+ if (!component) return collected
70
+
71
+ for (const dep of component.dependencies ?? []) {
72
+ if (visited.has(dep)) continue
73
+ visited.add(dep)
74
+ collectDependencyTree(dep, componentList, collected, visited)
75
+ collected.push(dep)
76
+ }
77
+
78
+ return collected
79
+ }
80
+
81
+ module.exports = {
82
+ getPackageRoot,
83
+ getPackageVersion,
84
+ getRegistryPath,
85
+ loadComponentRegistry,
86
+ getTailjngConfig,
87
+ getComponentsBasePath,
88
+ collectDependencyTree,
89
+ }
@@ -0,0 +1,18 @@
1
+ // overwrite-policy.js — session overwrite strategy for component installs
2
+
3
+ /** @type {'ask' | 'all' | 'skip' | null} */
4
+ let policy = null
5
+
6
+ function getOverwritePolicy() {
7
+ return policy
8
+ }
9
+
10
+ function setOverwritePolicy(value) {
11
+ policy = value
12
+ }
13
+
14
+ function resetOverwritePolicy() {
15
+ policy = null
16
+ }
17
+
18
+ module.exports = { getOverwritePolicy, setOverwritePolicy, resetOverwritePolicy }
@@ -1,25 +1,15 @@
1
- // path-utils.js
1
+ // path-utils.js — maps registry paths to consumer app install locations
2
2
 
3
+ const fs = require("fs")
3
4
  const path = require("path")
5
+ const { getComponentsBasePath } = require("./lib-utils")
4
6
 
5
- /**
6
- * Extracts folder structure information from the component path
7
- * @param {string} componentPath - Path del componente (ej: "src/lib/components/alert/dialog-alert")
8
- * @returns {object} - Information about the folder structure
9
- */
10
- function parseComponentPath(componentPath) {
11
-
12
- // Remove the base prefix "src/lib/components/"
13
- const basePath = "src/lib/components/"
14
- const relativePath = componentPath.replace(basePath, "")
7
+ const COMPONENTS_SOURCE_PREFIX = "src/lib/components/"
15
8
 
16
- // Split the path into parts
9
+ function parseComponentPath(componentPath) {
10
+ const relativePath = componentPath.replace(COMPONENTS_SOURCE_PREFIX, "")
17
11
  const pathParts = relativePath.split("/")
18
-
19
- // The last element is the component name
20
12
  const componentFolder = pathParts[pathParts.length - 1]
21
-
22
- // All except the last element are subfolders
23
13
  const subfolders = pathParts.slice(0, -1)
24
14
 
25
15
  return {
@@ -30,23 +20,18 @@ function parseComponentPath(componentPath) {
30
20
  }
31
21
  }
32
22
 
33
- /**
34
- * Make sure to keep the folder structure
35
- * @param {string} projectRoot - Project root directory
36
- * @param {string} componentName - Component name
37
- * @param {string} componentPath - Original component path
38
- * @returns {string} - Full target path
39
- */
40
23
  function buildTargetPath(projectRoot, componentName, componentPath) {
41
24
  const pathInfo = parseComponentPath(componentPath)
25
+ const basePath = getComponentsBasePath(projectRoot)
42
26
 
43
27
  if (pathInfo.hasSubfolders) {
44
- // If it has subfolders, keep the structure
45
- return path.join(projectRoot, "src", "app", "tailjng", pathInfo.fullSubPath, componentName)
46
- } else {
47
- // If it doesn't have subfolders, use the original structure
48
- return path.join(projectRoot, "src", "app", "tailjng", componentName)
28
+ return path.join(basePath, pathInfo.fullSubPath, componentName)
49
29
  }
30
+ return path.join(basePath, componentName)
31
+ }
32
+
33
+ function isComponentInstalled(projectRoot, componentName, componentPath) {
34
+ return fs.existsSync(buildTargetPath(projectRoot, componentName, componentPath))
50
35
  }
51
36
 
52
- module.exports = { parseComponentPath, buildTargetPath }
37
+ module.exports = { parseComponentPath, buildTargetPath, isComponentInstalled }