@trineui/cli 0.1.1 → 0.1.2

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
@@ -31,6 +31,7 @@ Notes:
31
31
  - for `apps/demo`, `@trine/ui` resolves locally for delivered components while `@trine/ui/*` temporarily bridges non-localized components back to the authoring source
32
32
  - the delivered shared styling baseline is `tokens.css` + `trine-consumer.css`
33
33
  - the current proven target dependency baseline is Angular 21, Tailwind CSS v4, and `class-variance-authority`
34
+ - the current proven target shape accepts a global stylesheet entry such as `src/styles.scss`, `src/styles.css`, or `src/global.scss` when it is resolved from `angular.json`
34
35
  - use a Node LTS line supported by Angular 21 in the target repo; odd-numbered Node releases can build with warnings
35
36
  - when `package.json` is present in the target root, the CLI warns if Tailwind CSS v4 or `class-variance-authority` are missing
36
37
 
@@ -61,10 +61,7 @@ const DEMO_PROXY_EXPORT_LINES = [
61
61
  ];
62
62
  export function addComponent(manifest, options) {
63
63
  const targetRoot = path.resolve(options.cwd, options.target);
64
- const targetAppDir = path.join(targetRoot, 'src', 'app');
65
- const targetStylesEntry = path.join(targetRoot, 'src', 'styles.scss');
66
- const targetTsconfig = path.join(targetRoot, 'tsconfig.app.json');
67
- assertTargetShape(manifest.componentName, targetRoot, targetAppDir, targetStylesEntry, targetTsconfig);
64
+ const { targetStylesEntry, targetTsconfig } = resolveTargetShape(manifest.componentName, targetRoot);
68
65
  const componentDestDir = path.join(targetRoot, 'src', 'app', 'components', 'ui', manifest.componentName);
69
66
  const stylesDestDir = path.join(targetRoot, 'src', 'styles');
70
67
  const componentCopyTargets = manifest.sourceFiles.map((source) => ({
@@ -146,7 +143,10 @@ function ensureSharedStyleBaseline(targetRoot, stylesDestDir, componentLabel) {
146
143
  warnings,
147
144
  };
148
145
  }
149
- function assertTargetShape(componentName, targetRoot, targetAppDir, targetStylesEntry, targetTsconfig) {
146
+ function resolveTargetShape(componentName, targetRoot) {
147
+ const targetAppDir = path.join(targetRoot, 'src', 'app');
148
+ const targetTsconfig = path.join(targetRoot, 'tsconfig.app.json');
149
+ const targetStylesEntry = resolveStylesEntry(targetRoot);
150
150
  const missing = [];
151
151
  if (!existsSync(targetRoot)) {
152
152
  missing.push(targetRoot);
@@ -154,18 +154,25 @@ function assertTargetShape(componentName, targetRoot, targetAppDir, targetStyles
154
154
  if (!existsSync(targetAppDir)) {
155
155
  missing.push(targetAppDir);
156
156
  }
157
- if (!existsSync(targetStylesEntry)) {
158
- missing.push(targetStylesEntry);
157
+ if (!targetStylesEntry) {
158
+ missing.push(`${path.join(targetRoot, 'src', 'styles.scss')} or ${path.join(targetRoot, 'src', 'styles.css')} or the first resolvable build styles entry in ${path.join(targetRoot, 'angular.json')}`);
159
159
  }
160
160
  if (!existsSync(targetTsconfig)) {
161
161
  missing.push(targetTsconfig);
162
162
  }
163
163
  if (missing.length > 0) {
164
164
  throw new Error([
165
- `trine add ${componentName} requires an Angular app target with src/app, src/styles.scss, and tsconfig.app.json.`,
165
+ `trine add ${componentName} requires an Angular app target with src/app, a global stylesheet entry, and tsconfig.app.json.`,
166
166
  ...missing.map((file) => `- ${file}`),
167
167
  ].join('\n'));
168
168
  }
169
+ if (!targetStylesEntry) {
170
+ throw new Error(`trine add ${componentName} could not resolve a global stylesheet entry for ${targetRoot}.`);
171
+ }
172
+ return {
173
+ targetStylesEntry,
174
+ targetTsconfig,
175
+ };
169
176
  }
170
177
  function ensureLinesFile(filePath, lines) {
171
178
  const existing = existsSync(filePath) ? readFileSync(filePath, 'utf8') : '';
@@ -315,3 +322,41 @@ function readTargetDependencyWarnings(targetRoot, componentLabel) {
315
322
  function looksLikeTailwindV4(range) {
316
323
  return /(^|[^\d])4(\D|$)/.test(range);
317
324
  }
325
+ export function looksLikeAngularAppRoot(root) {
326
+ return (existsSync(path.join(root, 'src', 'app')) &&
327
+ existsSync(path.join(root, 'tsconfig.app.json')) &&
328
+ resolveStylesEntry(root) !== undefined);
329
+ }
330
+ function resolveStylesEntry(targetRoot) {
331
+ const conventionalStyles = ['src/styles.scss', 'src/styles.css'];
332
+ for (const relativePath of conventionalStyles) {
333
+ const absolutePath = path.join(targetRoot, relativePath);
334
+ if (existsSync(absolutePath)) {
335
+ return absolutePath;
336
+ }
337
+ }
338
+ const angularJsonPath = path.join(targetRoot, 'angular.json');
339
+ if (!existsSync(angularJsonPath)) {
340
+ return undefined;
341
+ }
342
+ try {
343
+ const angularJson = JSON.parse(readFileSync(angularJsonPath, 'utf8'));
344
+ for (const project of Object.values(angularJson.projects ?? {})) {
345
+ const styles = project.architect?.build?.options?.styles ?? project.targets?.build?.options?.styles ?? [];
346
+ for (const styleEntry of styles) {
347
+ const relativePath = typeof styleEntry === 'string' ? styleEntry : (styleEntry.input ?? undefined);
348
+ if (!relativePath) {
349
+ continue;
350
+ }
351
+ const absolutePath = path.join(targetRoot, relativePath);
352
+ if (existsSync(absolutePath)) {
353
+ return absolutePath;
354
+ }
355
+ }
356
+ }
357
+ }
358
+ catch {
359
+ return undefined;
360
+ }
361
+ return undefined;
362
+ }
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { existsSync, readdirSync } from 'node:fs';
2
+ import { readdirSync } from 'node:fs';
3
3
  import path from 'node:path';
4
+ import { looksLikeAngularAppRoot } from "./add-component.js";
4
5
  import { addButton } from "./add-button.js";
5
6
  const HELP_TEXT = `Usage:
6
7
  npx @trineui/cli@latest add button [--target <app-root>]
@@ -14,7 +15,7 @@ Defaults:
14
15
  Notes:
15
16
  - v0 supports Button only
16
17
  - external targets can run trine add button from the app root or pass --target /absolute/path/to/angular-app
17
- - the current proven target model is Angular 21 + src/app + src/styles.scss + tsconfig.app.json
18
+ - the current proven target model is Angular 21 + src/app + tsconfig.app.json + a global stylesheet entry such as src/styles.scss, src/styles.css, or src/global.scss resolved from angular.json
18
19
  - use a Node LTS line supported by Angular 21 in the target repo
19
20
  - the current proven styling/runtime baseline requires Tailwind CSS v4 and class-variance-authority in the target repo
20
21
  - consumer-owned component files fail clearly if they already exist
@@ -75,9 +76,6 @@ function isSupportedComponent(value) {
75
76
  function capitalize(value) {
76
77
  return value.charAt(0).toUpperCase() + value.slice(1);
77
78
  }
78
- function looksLikeAngularAppRoot(root) {
79
- return ['src/app', 'src/styles.scss', 'tsconfig.app.json'].every((relativePath) => existsSync(path.join(root, relativePath)));
80
- }
81
79
  function autoDetectTarget(cwd) {
82
80
  if (looksLikeAngularAppRoot(cwd)) {
83
81
  return '.';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trineui/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "Copy-paste ownership CLI for Trine UI components.",
6
6
  "main": "./dist/index.js",