@trineui/cli 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/README.md CHANGED
@@ -16,7 +16,8 @@ Notes:
16
16
 
17
17
  - `button` is the only supported component in this public-style baseline
18
18
  - omitting `--target` uses the current directory when it already matches the supported Angular app shape
19
- - when the current directory is not a supported Angular app target, omitting `--target` falls back to `apps/demo` for local Trine repo verification only
19
+ - when the current directory is not a supported Angular app target, the CLI auto-detects a single Angular app target under the current directory and proceeds automatically
20
+ - when multiple Angular app targets are found, the CLI stops and asks for `--target <app-root>`
20
21
  - external targets can run `trine add button` from the app root or pass an explicit app root such as `--target /absolute/path/to/angular-app`
21
22
  - the canonical public package name is `@trineui/cli`
22
23
  - the CLI command exposed through the package bin is still `trine`
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url';
4
4
  import ts from 'typescript';
5
5
  const STYLE_SOURCE_FILES = ['styles/tokens.css', 'styles/trine-consumer.css'];
6
6
  const TEMPLATE_ROOT = fileURLToPath(new URL('../templates/', import.meta.url));
7
+ const LOCAL_REPO_DEMO_ROOT = path.resolve(TEMPLATE_ROOT, '../../../apps/demo');
7
8
  const STYLE_IMPORT_LINE = "@import './styles/trine-consumer.css';";
8
9
  const DEMO_BRIDGE_COMMENT = '// Temporary demo verification bridge: delivered local components resolve locally; other components still re-export from the authoring source.';
9
10
  const DEMO_LOCAL_COMPONENTS = [
@@ -94,7 +95,7 @@ export function addComponent(manifest, options) {
94
95
  updatedFiles.push(toTargetRelativePath(targetRoot, componentBarrelPath));
95
96
  }
96
97
  const uiRootBarrelPath = path.join(targetRoot, 'src', 'app', 'components', 'ui', 'index.ts');
97
- const uiRootUpdated = isDemoTarget(targetRoot, options.cwd)
98
+ const uiRootUpdated = isDemoTarget(targetRoot)
98
99
  ? rewriteDemoUiRootBarrel(uiRootBarrelPath)
99
100
  : ensureLinesFile(uiRootBarrelPath, [manifest.uiExportLine]);
100
101
  if (uiRootUpdated) {
@@ -110,7 +111,7 @@ export function addComponent(manifest, options) {
110
111
  if (stylesResult.authoringImportStillPresent) {
111
112
  warnings.push(`${toTargetRelativePath(targetRoot, targetStylesEntry)} still imports @trine/ui/styles/trine.css for the broader demo authoring baseline.`);
112
113
  }
113
- if (isDemoTarget(targetRoot, options.cwd)) {
114
+ if (isDemoTarget(targetRoot)) {
114
115
  warnings.push('apps/demo keeps non-localized components on a temporary @trine/ui/* bridge back to packages/ui so the full demo app can still build while delivered components resolve locally.');
115
116
  }
116
117
  else {
@@ -213,14 +214,14 @@ function ensureTsconfigAlias(tsconfigPath, targetRoot, cwd) {
213
214
  const config = (parsed.config ?? {});
214
215
  config.compilerOptions ??= {};
215
216
  config.compilerOptions.paths ??= {};
216
- const aliasTarget = isDemoTarget(targetRoot, cwd)
217
- ? toPosixPath(path.relative(cwd, path.join(targetRoot, 'src', 'app', 'components', 'ui', 'index.ts')))
217
+ const aliasTarget = isDemoTarget(targetRoot)
218
+ ? toPosixPath(path.relative(process.cwd(), path.join(targetRoot, 'src', 'app', 'components', 'ui', 'index.ts')))
218
219
  : toConfigRelativePath(path.relative(path.dirname(tsconfigPath), path.join(targetRoot, 'src', 'app', 'components', 'ui', 'index.ts')));
219
220
  const wildcardTarget = 'packages/ui/*';
220
221
  const currentAlias = config.compilerOptions.paths['@trine/ui'];
221
222
  const currentWildcardAlias = config.compilerOptions.paths['@trine/ui/*'];
222
223
  const aliasIsCurrent = Array.isArray(currentAlias) && currentAlias.length === 1 && currentAlias[0] === aliasTarget;
223
- const wildcardIsCurrent = !isDemoTarget(targetRoot, cwd) ||
224
+ const wildcardIsCurrent = !isDemoTarget(targetRoot) ||
224
225
  (Array.isArray(currentWildcardAlias) &&
225
226
  currentWildcardAlias.length === 1 &&
226
227
  currentWildcardAlias[0] === wildcardTarget);
@@ -228,7 +229,7 @@ function ensureTsconfigAlias(tsconfigPath, targetRoot, cwd) {
228
229
  return false;
229
230
  }
230
231
  config.compilerOptions.paths['@trine/ui'] = [aliasTarget];
231
- if (isDemoTarget(targetRoot, cwd)) {
232
+ if (isDemoTarget(targetRoot)) {
232
233
  config.compilerOptions.paths['@trine/ui/*'] = [wildcardTarget];
233
234
  }
234
235
  else if ('@trine/ui/*' in config.compilerOptions.paths) {
@@ -265,8 +266,9 @@ function ensureStylesImport(stylesPath) {
265
266
  authoringImportStillPresent: current.includes("@import '@trine/ui/styles/trine.css';"),
266
267
  };
267
268
  }
268
- function isDemoTarget(targetRoot, cwd) {
269
- return path.resolve(targetRoot) === path.resolve(cwd, 'apps/demo');
269
+ function isDemoTarget(targetRoot) {
270
+ return (existsSync(LOCAL_REPO_DEMO_ROOT) &&
271
+ path.resolve(targetRoot) === path.resolve(LOCAL_REPO_DEMO_ROOT));
270
272
  }
271
273
  function toPosixPath(filePath) {
272
274
  return filePath.split(path.sep).join(path.posix.sep);
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync } from 'node:fs';
2
+ import { existsSync, readdirSync } from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { addButton } from "./add-button.js";
5
5
  const HELP_TEXT = `Usage:
@@ -8,7 +8,8 @@ const HELP_TEXT = `Usage:
8
8
 
9
9
  Defaults:
10
10
  - current directory when it matches the supported Angular app target shape
11
- - otherwise apps/demo for local Trine repo verification only
11
+ - otherwise auto-detect a single Angular app target under the current directory
12
+ - when multiple Angular app targets are found, re-run with --target <app-root>
12
13
 
13
14
  Notes:
14
15
  - v0 supports Button only
@@ -29,7 +30,7 @@ function main(argv) {
29
30
  if (!isSupportedComponent(component)) {
30
31
  throw new Error(component ? `Unsupported component: ${component}\n\n${HELP_TEXT}` : HELP_TEXT);
31
32
  }
32
- const target = readTarget(rest) ?? defaultTarget(process.cwd());
33
+ const target = readTarget(rest) ?? autoDetectTarget(process.cwd());
33
34
  const result = addButton({
34
35
  target,
35
36
  cwd: process.cwd(),
@@ -48,6 +49,7 @@ function printSuccess(component, result) {
48
49
  const relativeTarget = path.relative(process.cwd(), result.targetRoot) || '.';
49
50
  const displayTarget = relativeTarget.startsWith('..') ? result.targetRoot : relativeTarget;
50
51
  const componentLabel = capitalize(component);
52
+ const isRepoDemoVerification = result.warnings.some((warning) => warning.includes('temporary @trine/ui/* bridge'));
51
53
  const lines = [
52
54
  `trine add ${component} completed.`,
53
55
  `Target: ${displayTarget}`,
@@ -62,7 +64,7 @@ function printSuccess(component, result) {
62
64
  lines.push('', 'Warnings:', ...result.warnings.map((warning) => `- ${warning}`));
63
65
  }
64
66
  lines.push('', 'Manual next steps:', `- Ensure the target repo has Tailwind CSS v4 and class-variance-authority installed before building the delivered ${componentLabel}.`, `- Build the target app and confirm ${componentLabel} imports resolve through the local @trine/ui alias.`, `- Review the copied ${component}.skin.ts and tokens.css if you want local consumer customization.`);
65
- if (relativeTarget === 'apps/demo') {
67
+ if (isRepoDemoVerification) {
66
68
  lines.push('- Open /validation-shell and review the CLI delivery proof section for the temporary demo verification path.');
67
69
  }
68
70
  console.log(lines.join('\n'));
@@ -73,12 +75,56 @@ function isSupportedComponent(value) {
73
75
  function capitalize(value) {
74
76
  return value.charAt(0).toUpperCase() + value.slice(1);
75
77
  }
76
- function defaultTarget(cwd) {
77
- return looksLikeAngularAppRoot(cwd) ? '.' : 'apps/demo';
78
- }
79
78
  function looksLikeAngularAppRoot(root) {
80
79
  return ['src/app', 'src/styles.scss', 'tsconfig.app.json'].every((relativePath) => existsSync(path.join(root, relativePath)));
81
80
  }
81
+ function autoDetectTarget(cwd) {
82
+ if (looksLikeAngularAppRoot(cwd)) {
83
+ return '.';
84
+ }
85
+ const matches = findAngularAppTargets(cwd);
86
+ if (matches.length === 1) {
87
+ return matches[0];
88
+ }
89
+ if (matches.length > 1) {
90
+ throw new Error([
91
+ 'Multiple Angular app targets were found under the current directory. Re-run with --target <app-root>:',
92
+ ...matches.map((match) => `- ${match}`),
93
+ ].join('\n'));
94
+ }
95
+ return '.';
96
+ }
97
+ function findAngularAppTargets(root) {
98
+ const matches = new Set();
99
+ walkForAngularApps(root, root, matches);
100
+ return [...matches].sort((left, right) => left.localeCompare(right));
101
+ }
102
+ function walkForAngularApps(root, currentDir, matches) {
103
+ for (const entry of readdirSync(currentDir, { withFileTypes: true })) {
104
+ if (entry.isDirectory()) {
105
+ if (shouldSkipDirectory(entry.name)) {
106
+ continue;
107
+ }
108
+ walkForAngularApps(root, path.join(currentDir, entry.name), matches);
109
+ continue;
110
+ }
111
+ if (!entry.isFile() || entry.name !== 'tsconfig.app.json') {
112
+ continue;
113
+ }
114
+ const candidateRoot = currentDir;
115
+ if (!looksLikeAngularAppRoot(candidateRoot)) {
116
+ continue;
117
+ }
118
+ const relativeRoot = path.relative(root, candidateRoot) || '.';
119
+ matches.add(toPosixPath(relativeRoot));
120
+ }
121
+ }
122
+ function shouldSkipDirectory(name) {
123
+ return ['.angular', '.git', '.playwright-cli', 'dist', 'node_modules', 'output'].includes(name);
124
+ }
125
+ function toPosixPath(filePath) {
126
+ return filePath.split(path.sep).join(path.posix.sep);
127
+ }
82
128
  try {
83
129
  main(process.argv.slice(2));
84
130
  }
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@trineui/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "Copy-paste ownership CLI for Trine UI components.",
6
6
  "main": "./dist/index.js",
7
7
  "bin": {
8
- "trine": "./bin/trine.js"
8
+ "trine": "bin/trine.js"
9
9
  },
10
10
  "scripts": {
11
11
  "build": "tsc -p tsconfig.build.json",