@trineui/cli 0.2.0 → 0.4.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
@@ -1,54 +1,204 @@
1
1
  # @trineui/cli
2
2
 
3
- Canonical public package:
3
+ `@trineui/cli` is the public Trine CLI for the current Button-first baseline.
4
+
5
+ Current public baseline:
6
+
7
+ - guided `init`
8
+ - `add button`
9
+ - Button is the only public component baseline in scope
10
+ - Angular has the strongest proof coverage right now
11
+
12
+ Canonical public flow:
4
13
 
5
14
  ```bash
15
+ npx @trineui/cli@latest init
6
16
  npx @trineui/cli@latest add button
7
17
  ```
8
18
 
9
- Registry status note:
19
+ `init` is guided by default. Use `--yes` for non-interactive mode.
20
+
21
+ ## What Works Right Now
22
+
23
+ The current public Trine baseline is intentionally narrow.
24
+
25
+ It supports:
26
+
27
+ - preparing a supported Angular app with `init`
28
+ - delivering a consumer-owned Button with `add button`
29
+ - wiring a local `@trine/ui` alias into the target app
30
+ - injecting the Trine styling baseline into the target app's real global stylesheet entry
31
+ - local customization by editing `button.skin.ts` or adding semantic variable overrides below the Trine-managed block
32
+
33
+ It does not currently support:
34
+
35
+ - more components than Button
36
+ - automatic prerequisite installation
37
+ - automatic usage-surface scaffolding
38
+ - broad framework coverage claims beyond the current Angular-first baseline
39
+
40
+ ## Before You Start
41
+
42
+ ### Supported target shape
43
+
44
+ The current proven target shape is an Angular app with:
45
+
46
+ - `src/app`
47
+ - `tsconfig.app.json`
48
+ - a valid global stylesheet entry such as:
49
+ - `src/styles.scss`
50
+ - `src/styles.css`
51
+ - `src/global.scss`
52
+ - or a build styles entry resolved from `angular.json`
53
+
54
+ `init` can distinguish `angular` and `ionic-angular`, but Angular has the strongest live proof coverage today.
55
+
56
+ ### Manual prerequisites
57
+
58
+ The live CLI still expects these prerequisites to be installed manually in the target repo.
59
+
60
+ For the current Button baseline, these are hard requirements rather than optional enhancements:
61
+
62
+ ```bash
63
+ npm install class-variance-authority
64
+ npm install -D tailwindcss@^4 @tailwindcss/postcss postcss
65
+ ```
66
+
67
+ The current baseline also expects PostCSS configuration in the target repo:
68
+
69
+ `.postcssrc.json`
70
+
71
+ ```json
72
+ {
73
+ "plugins": {
74
+ "@tailwindcss/postcss": {}
75
+ }
76
+ }
77
+ ```
78
+
79
+ Use a Node LTS line supported by Angular 21 in the target repo. Odd-numbered Node releases can build with warnings.
80
+
81
+ For the styling-layer contract behind these requirements, see:
82
+
83
+ - `docs/01-overview/05-styling-contract.md`
84
+
85
+ ## Quickstart
86
+
87
+ ### 1. Run guided init
88
+
89
+ From the app root:
90
+
91
+ ```bash
92
+ npx @trineui/cli@latest init
93
+ ```
94
+
95
+ If you prefer non-interactive mode:
10
96
 
11
- - live npm `@latest` is currently `0.1.2`
12
- - repo-local next publish candidate is `0.2.0`
13
- - the live registry package currently exposes `add button`
14
- - `init` is implemented in this repo and included in the next publish candidate, but it is not available from the live npm package until publish succeeds
97
+ ```bash
98
+ npx @trineui/cli@latest init --yes
99
+ ```
15
100
 
16
- Repository-local current command surface:
101
+ If you are inside a workspace root or want to target a specific app:
17
102
 
18
103
  ```bash
19
- trine init --target <app-root>
20
- trine add button --target <app-root>
104
+ npx @trineui/cli@latest init --target /absolute/path/to/app
21
105
  ```
22
106
 
23
- Notes:
107
+ `init` currently:
108
+
109
+ - detects the target app
110
+ - detects `angular` vs `ionic-angular`
111
+ - resolves the target stylesheet entry
112
+ - previews file changes before mutation
113
+ - injects a named Trine-managed styling block into the resolved global stylesheet entry
114
+ - ensures `src/app/components/ui/index.ts`
115
+ - ensures local `@trine/ui` in `tsconfig.app.json`
116
+ - ensures the stylesheet block includes semantic tokens, `.dark` overrides, and Tailwind v4 `@theme inline` mapping
117
+
118
+ `init` does not currently install Tailwind/PostCSS/CVA or generate `.postcssrc.json` for you.
119
+
120
+ Rerunning `init` on an already prepared target should no-op cleanly.
121
+
122
+ ### 2. Deliver Button
123
+
124
+ ```bash
125
+ npx @trineui/cli@latest add button
126
+ ```
127
+
128
+ Or explicitly:
129
+
130
+ ```bash
131
+ npx @trineui/cli@latest add button --target /absolute/path/to/app
132
+ ```
133
+
134
+ `add button` currently delivers:
135
+
136
+ - `src/app/components/ui/button/button.ts`
137
+ - `src/app/components/ui/button/button.html`
138
+ - `src/app/components/ui/button/button.skin.ts`
139
+ - `src/app/components/ui/button/index.ts`
140
+ - `src/app/components/ui/index.ts`
141
+
142
+ It fails clearly if consumer-owned Button files already exist. It does not silently overwrite them.
24
143
 
25
- - `init` is now the preferred first step for project readiness; `add button` still works without it for backward compatibility
26
- - `button` is the only supported component in this public-style baseline
27
- - omitting `--target` uses the current directory when it already matches the supported Angular app shape
28
- - 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
29
- - when multiple Angular app targets are found, the CLI stops and asks for `--target <app-root>`
30
- - 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`
31
- - `init` detects `angular` and `ionic-angular` targets conservatively; unsupported targets fail clearly
32
- - the canonical public package name is `@trineui/cli`
33
- - the CLI command exposed through the package bin is still `trine`
34
- - `apps/consumer-fixture` is the first separate-target proof and does not use the demo-only `@trine/ui/*` bridge
35
- - `/tmp/trine-button-publish-proof` is the latest truly external packaged-proof repo outside the monorepo
36
- - packaged/public-style proof uses a packed local tarball to simulate `npx @trineui/cli@latest add button`
37
- - the packaged CLI ships compiled runtime files plus Button templates so it can execute from `node_modules` in a real `npx`-style flow
38
- - `init` ensures `src/styles/tokens.css`, `src/styles/trine-consumer.css`, `src/app/components/ui/index.ts`, the local `@trine/ui` alias, and local stylesheet wiring
39
- - consumer-owned component destination files cause a clear failure
40
- - existing shared baseline files (`tokens.css` and `trine-consumer.css`) are preserved so a second component can be added into the same target repo
41
- - the command copies consumer-owned source instead of wiring runtime back to `packages/ui`
42
- - for `apps/demo`, `@trine/ui` resolves locally for delivered components while `@trine/ui/*` temporarily bridges non-localized components back to the authoring source
43
- - the delivered shared styling baseline is `tokens.css` + `trine-consumer.css`
44
- - the current proven target dependency baseline is Angular 21, Tailwind CSS v4, and `class-variance-authority`
45
- - 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`
46
- - use a Node LTS line supported by Angular 21 in the target repo; odd-numbered Node releases can build with warnings
47
- - when `package.json` is present in the target root, the CLI warns if Tailwind CSS v4 or `class-variance-authority` are missing
144
+ ### 3. Render a small proof
48
145
 
49
- Local package proof equivalent:
146
+ Import `TrineButton` from local `@trine/ui` and render a small proof surface:
147
+
148
+ ```ts
149
+ import { Component } from '@angular/core';
150
+ import { TrineButton } from '@trine/ui';
151
+
152
+ @Component({
153
+ selector: 'app-root',
154
+ imports: [TrineButton],
155
+ template: `
156
+ <trine-button>Primary action</trine-button>
157
+ <trine-button variant="secondary">Secondary action</trine-button>
158
+ <trine-button [loading]="true">Saving</trine-button>
159
+ `,
160
+ })
161
+ export class App {}
162
+ ```
163
+
164
+ ### 4. Build the target app
50
165
 
51
166
  ```bash
52
- npx --yes --package /absolute/path/to/trineui-cli-<version>.tgz trine init
53
- npx --yes --package /absolute/path/to/trineui-cli-<version>.tgz trine add button
167
+ npm run build
54
168
  ```
169
+
170
+ Successful verification usually means:
171
+
172
+ - the app builds
173
+ - `@trine/ui` resolves to local consumer source
174
+ - the resolved stylesheet entry contains a Trine-managed baseline block
175
+ - Button renders from local delivered files
176
+
177
+ ## Customize Locally
178
+
179
+ Trine uses a copy-paste ownership model.
180
+
181
+ After delivery, the consumer repo owns the visual layer.
182
+
183
+ The two main local customization paths are:
184
+
185
+ - edit `src/app/components/ui/button/button.skin.ts`
186
+ - add semantic variable overrides below the Trine-managed block in the resolved global stylesheet entry
187
+
188
+ This is the intended model. Trine does not keep runtime ownership of the Button visuals after delivery.
189
+
190
+ ## Current Limitations
191
+
192
+ - Button-only public baseline
193
+ - manual prerequisites still required
194
+ - usage surface is not scaffolded automatically
195
+ - Angular has the strongest live proof coverage; Ionic Angular coverage is narrower
196
+ - multiple target apps still require `--target` or guided selection
197
+ - `src/styles.scss` and `src/global.scss` currently work, but Angular 21 surfaces a Sass deprecation warning for `@import 'tailwindcss';`
198
+ - the baseline is intentionally narrow and not yet a broader component platform
199
+
200
+ ## Full Walkthrough
201
+
202
+ For a fuller repo-level walkthrough of the current baseline, see:
203
+
204
+ - `docs/01-overview/04-cli-getting-started.md`
@@ -1,6 +1,6 @@
1
1
  import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import path from 'node:path';
3
- import { TEMPLATE_ROOT, ensureLinesFile, ensureSharedStyleBaseline, ensureStylesImport, ensureTsconfigAlias, isDemoTarget, readTargetDependencyWarnings, resolveSupportedProjectTarget, toTargetRelativePath, } from "./project.js";
3
+ import { TEMPLATE_ROOT, ensureLinesFile, ensureTrineStylesheetBaseline, ensureTsconfigAlias, isDemoTarget, readTargetDependencyWarnings, resolveSupportedProjectTarget, toTargetRelativePath, } from "./project.js";
4
4
  const DEMO_BRIDGE_COMMENT = '// Temporary demo verification bridge: delivered local components resolve locally; other components still re-export from the authoring source.';
5
5
  const DEMO_LOCAL_COMPONENTS = [
6
6
  {
@@ -76,10 +76,9 @@ export function addComponent(manifest, options) {
76
76
  copyFileSync(source, destination);
77
77
  }
78
78
  const componentCopiedFiles = componentCopyTargets.map(({ destination }) => toTargetRelativePath(targetRoot, destination));
79
- const sharedStylesResult = ensureSharedStyleBaseline(targetRoot, manifest.componentLabel);
80
- const copiedFiles = [...componentCopiedFiles, ...sharedStylesResult.createdFiles];
79
+ const copiedFiles = [...componentCopiedFiles];
81
80
  const updatedFiles = [];
82
- const warnings = [...sharedStylesResult.warnings];
81
+ const warnings = [];
83
82
  const componentBarrelPath = path.join(componentDestDir, 'index.ts');
84
83
  if (ensureLinesFile(componentBarrelPath, [manifest.barrelLine])) {
85
84
  updatedFiles.push(toTargetRelativePath(targetRoot, componentBarrelPath));
@@ -94,12 +93,13 @@ export function addComponent(manifest, options) {
94
93
  if (ensureTsconfigAlias(targetTsconfig, targetRoot, options.cwd)) {
95
94
  updatedFiles.push(toTargetRelativePath(targetRoot, targetTsconfig));
96
95
  }
97
- const stylesResult = ensureStylesImport(targetStylesEntry);
96
+ const stylesResult = ensureTrineStylesheetBaseline(targetRoot, targetStylesEntry);
98
97
  if (stylesResult.updated) {
99
98
  updatedFiles.push(toTargetRelativePath(targetRoot, targetStylesEntry));
100
99
  }
100
+ warnings.push(...stylesResult.warnings);
101
101
  if (stylesResult.authoringImportStillPresent) {
102
- warnings.push(`${toTargetRelativePath(targetRoot, targetStylesEntry)} still imports @trine/ui/styles/trine.css for the broader demo authoring baseline.`);
102
+ warnings.push(`${toTargetRelativePath(targetRoot, targetStylesEntry)} keeps the repo-local @trine/ui/styles/trine.css authoring entry so apps/demo can still validate non-localized components during local development.`);
103
103
  }
104
104
  if (isDemoTarget(targetRoot)) {
105
105
  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.');
package/dist/index.js CHANGED
@@ -1,38 +1,60 @@
1
1
  #!/usr/bin/env node
2
2
  import path from 'node:path';
3
3
  import { addButton } from "./add-button.js";
4
- import { initProject } from "./init.js";
5
- import { findAngularAppTargets, looksLikeAngularAppRoot } from "./project.js";
4
+ import { applyInitPlan, planInitProject } from "./init.js";
5
+ import { chooseFromList, confirmAction } from "./prompt.js";
6
+ import { findAngularAppTargets, inspectProjectTarget, looksLikeAngularAppRoot } from "./project.js";
6
7
  const HELP_TEXT = `Usage:
7
- npx @trineui/cli@latest init [--target <app-root>]
8
+ npx @trineui/cli@latest init [--target <app-root>] [--yes]
8
9
  npx @trineui/cli@latest add button [--target <app-root>]
9
- trine init [--target <app-root>]
10
+ trine init [--target <app-root>] [--yes]
10
11
  trine add button [--target <app-root>]
11
12
 
12
13
  Defaults:
13
- - current directory when it already matches the supported Trine app target shape
14
- - otherwise auto-detect a single supported Angular app target under the current directory
15
- - when multiple supported app targets are found, re-run with --target <app-root>
14
+ - init is guided by default and asks for confirmation before mutating project files
15
+ - init --yes keeps a non-interactive fast path for automation and scripting
16
+ - current directory is used when it already matches the supported Trine app target shape
17
+ - otherwise init/add auto-detect a single supported Angular app target under the current directory
18
+ - when multiple supported app targets are found, guided init lets you choose and add still asks for --target
16
19
 
17
20
  Notes:
18
21
  - v0 supports init plus add button only
19
- - init owns target detection, framework detection, stylesheet resolution, baseline files, local @trine/ui alias setup, and local stylesheet wiring
20
- - add button still works without init for backward compatibility, but init is now the preferred first step
22
+ - init owns target detection, framework detection, stylesheet resolution, Trine-managed global stylesheet injection, and local @trine/ui alias setup
23
+ - add button still works without init for backward compatibility, but init is the preferred first step
21
24
  - 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 directly or from angular.json
22
25
  - v0 distinguishes angular and ionic-angular targets; unsupported frameworks fail clearly
23
- - the current proven styling/runtime baseline requires Tailwind CSS v4 and class-variance-authority in the target repo
26
+ - the current proven styling/runtime baseline requires Tailwind CSS v4, PostCSS with @tailwindcss/postcss, and class-variance-authority in the target repo
24
27
  - consumer-owned component files fail clearly if they already exist
25
- - shared styling baseline files are copied when missing and preserved when they already exist
28
+ - init injects a Trine-managed styling block into the resolved global stylesheet entry instead of copying separate shared CSS files
26
29
  - @trine/ui is configured as a consumer-local alias inside the target app
27
30
  - apps/demo keeps a temporary @trine/ui/* bridge for non-localized components during local repo verification`;
28
31
  const SUPPORTED_COMPONENTS = ['button'];
29
- function main(argv) {
32
+ async function main(argv) {
30
33
  const [command, secondArg, ...rest] = argv;
31
34
  if (command === 'init') {
32
- const selection = resolveTargetSelection(process.cwd(), argv.slice(1), 'trine init');
33
- const result = initProject({
35
+ const flags = parseFlags(argv.slice(1), ['--target', '--yes']);
36
+ const selection = flags.yes
37
+ ? resolveNonInteractiveInitSelection(process.cwd(), flags.target)
38
+ : await resolveGuidedInitSelection(process.cwd(), flags.target);
39
+ const plan = planInitProject({
34
40
  target: selection.target,
35
41
  cwd: process.cwd(),
42
+ targetStylesEntry: selection.targetStylesEntry,
43
+ });
44
+ if (!hasInitMutations(plan)) {
45
+ printInitNoop(plan, selection);
46
+ return;
47
+ }
48
+ if (!flags.yes) {
49
+ printInitPreview(plan, selection);
50
+ const shouldProceed = await confirmAction('Proceed? (Y/n)');
51
+ if (!shouldProceed) {
52
+ console.log('trine init cancelled. No changes were made.');
53
+ return;
54
+ }
55
+ }
56
+ const result = applyInitPlan(plan, {
57
+ cwd: process.cwd(),
36
58
  });
37
59
  printInitSuccess(result, selection);
38
60
  return;
@@ -43,23 +65,54 @@ function main(argv) {
43
65
  if (!isSupportedComponent(secondArg)) {
44
66
  throw new Error(secondArg ? `Unsupported component: ${secondArg}\n\n${HELP_TEXT}` : HELP_TEXT);
45
67
  }
46
- const selection = resolveTargetSelection(process.cwd(), rest, `trine add ${secondArg}`);
68
+ const flags = parseFlags(rest, ['--target']);
69
+ const selection = resolveTargetSelection(process.cwd(), flags.target, `trine add ${secondArg}`);
47
70
  const result = addButton({
48
71
  target: selection.target,
49
72
  cwd: process.cwd(),
50
73
  });
51
74
  printAddSuccess(secondArg, result, selection);
52
75
  }
53
- function readTarget(argv) {
76
+ function resolveNonInteractiveInitSelection(cwd, explicitTarget) {
77
+ const selection = resolveTargetSelection(cwd, explicitTarget, 'trine init');
78
+ const targetRoot = path.resolve(cwd, selection.target);
79
+ const inspection = inspectProjectTarget(targetRoot);
80
+ if (inspection.framework !== 'unsupported' &&
81
+ inspection.targetStylesEntryCandidates.length > 1 &&
82
+ inspection.targetStylesEntryResolution === 'default') {
83
+ const displayTarget = selection.mode === 'cwd' ? '.' : toDisplayTarget(cwd, targetRoot);
84
+ throw new Error([
85
+ `Multiple plausible global stylesheet entries were found for target (${displayTarget}). Re-run without --yes to choose one interactively:`,
86
+ ...inspection.targetStylesEntryCandidates.map((candidate) => `- ${toDisplayFilePath(targetRoot, candidate)}`),
87
+ ].join('\n'));
88
+ }
89
+ return selection;
90
+ }
91
+ function parseFlags(argv, allowedFlags) {
92
+ const flags = {
93
+ yes: false,
94
+ };
54
95
  for (let index = 0; index < argv.length; index += 1) {
55
- if (argv[index] === '--target') {
56
- return argv[index + 1];
96
+ const token = argv[index];
97
+ if (!allowedFlags.includes(token)) {
98
+ throw new Error(`Unsupported flag: ${token}\n\n${HELP_TEXT}`);
99
+ }
100
+ if (token === '--yes') {
101
+ flags.yes = true;
102
+ continue;
103
+ }
104
+ if (token === '--target') {
105
+ const target = argv[index + 1];
106
+ if (!target || target.startsWith('--')) {
107
+ throw new Error(`--target requires a value.\n\n${HELP_TEXT}`);
108
+ }
109
+ flags.target = target;
110
+ index += 1;
57
111
  }
58
112
  }
59
- return undefined;
113
+ return flags;
60
114
  }
61
- function resolveTargetSelection(cwd, argv, commandLabel) {
62
- const explicitTarget = readTarget(argv);
115
+ function resolveTargetSelection(cwd, explicitTarget, commandLabel) {
63
116
  if (explicitTarget) {
64
117
  return {
65
118
  target: explicitTarget,
@@ -90,6 +143,92 @@ function resolveTargetSelection(cwd, argv, commandLabel) {
90
143
  mode: 'cwd',
91
144
  };
92
145
  }
146
+ async function resolveGuidedInitSelection(cwd, explicitTarget) {
147
+ const targetSelection = explicitTarget
148
+ ? {
149
+ target: explicitTarget,
150
+ mode: 'explicit',
151
+ }
152
+ : await chooseInitTarget(cwd);
153
+ const targetRoot = path.resolve(cwd, targetSelection.target);
154
+ const inspection = inspectProjectTarget(targetRoot);
155
+ if (inspection.framework === 'unsupported') {
156
+ return targetSelection;
157
+ }
158
+ if (inspection.targetStylesEntryCandidates.length > 1 &&
159
+ inspection.targetStylesEntryResolution === 'default') {
160
+ const targetStylesEntry = await chooseFromList('Multiple plausible global stylesheet entries were found:', inspection.targetStylesEntryCandidates, {
161
+ renderItem: (item, index) => `${String(index + 1)}. ${toDisplayFilePath(targetRoot, item)}`,
162
+ });
163
+ return {
164
+ ...targetSelection,
165
+ targetStylesEntry,
166
+ };
167
+ }
168
+ return targetSelection;
169
+ }
170
+ async function chooseInitTarget(cwd) {
171
+ if (looksLikeAngularAppRoot(cwd)) {
172
+ return {
173
+ target: '.',
174
+ mode: 'cwd',
175
+ };
176
+ }
177
+ const matches = findAngularAppTargets(cwd);
178
+ if (matches.length === 1) {
179
+ return {
180
+ target: matches[0],
181
+ mode: 'auto-detected',
182
+ };
183
+ }
184
+ if (matches.length > 1) {
185
+ const selected = await chooseFromList('Multiple supported apps found:', matches);
186
+ return {
187
+ target: selected,
188
+ mode: 'selected',
189
+ };
190
+ }
191
+ return {
192
+ target: '.',
193
+ mode: 'cwd',
194
+ };
195
+ }
196
+ function printInitPreview(plan, selection) {
197
+ const displayTarget = toDisplayTarget(process.cwd(), plan.targetRoot);
198
+ const stylesheetDisplay = toDisplayFilePath(plan.targetRoot, plan.targetStylesEntry);
199
+ const lines = [
200
+ `Detected target: ${displayTarget}`,
201
+ `Detected framework: ${plan.framework}`,
202
+ `Detected stylesheet entry: ${stylesheetDisplay}`,
203
+ ];
204
+ if (selection.mode === 'auto-detected' || selection.mode === 'selected') {
205
+ lines.push(`Selected app: ${displayTarget}`);
206
+ }
207
+ lines.push('', 'Trine will:', ...renderPlannedInitActions(plan));
208
+ if (plan.warnings.length > 0) {
209
+ lines.push('', 'Warnings:', ...plan.warnings.map((warning) => `- ${warning}`));
210
+ }
211
+ console.log(lines.join('\n'));
212
+ }
213
+ function printInitNoop(plan, selection) {
214
+ const displayTarget = toDisplayTarget(process.cwd(), plan.targetRoot);
215
+ const stylesheetDisplay = toDisplayFilePath(plan.targetRoot, plan.targetStylesEntry);
216
+ const lines = [
217
+ 'trine init found the Trine baseline already in place.',
218
+ `Target: ${displayTarget}`,
219
+ `Framework: ${plan.framework}`,
220
+ `Resolved stylesheet entry: ${stylesheetDisplay}`,
221
+ ];
222
+ if (selection.mode === 'auto-detected' || selection.mode === 'selected') {
223
+ lines.push(`Selected app: ${displayTarget}`);
224
+ }
225
+ lines.push('', 'Already in place:', ...renderFileGroup(plan.reusedFiles, '- nothing to report'));
226
+ if (plan.warnings.length > 0) {
227
+ lines.push('', 'Warnings:', ...plan.warnings.map((warning) => `- ${warning}`));
228
+ }
229
+ lines.push('', 'No changes were made.');
230
+ console.log(lines.join('\n'));
231
+ }
93
232
  function printInitSuccess(result, selection) {
94
233
  const displayTarget = toDisplayTarget(process.cwd(), result.targetRoot);
95
234
  const stylesheetDisplay = toDisplayFilePath(result.targetRoot, result.targetStylesEntry);
@@ -99,14 +238,14 @@ function printInitSuccess(result, selection) {
99
238
  `Framework: ${result.framework}`,
100
239
  `Resolved stylesheet entry: ${stylesheetDisplay}`,
101
240
  ];
102
- if (selection.mode === 'auto-detected') {
241
+ if (selection.mode === 'auto-detected' || selection.mode === 'selected') {
103
242
  lines.push(`Selected app: ${displayTarget}`);
104
243
  }
105
244
  lines.push('', 'Created:', ...renderFileGroup(result.createdFiles, '- nothing created'), '', 'Reused:', ...renderFileGroup(result.reusedFiles, '- nothing reused'), '', 'Updated:', ...renderFileGroup(result.updatedFiles, '- nothing updated'));
106
245
  if (result.warnings.length > 0) {
107
246
  lines.push('', 'Warnings:', ...result.warnings.map((warning) => `- ${warning}`));
108
247
  }
109
- lines.push('', 'Manual next steps:', '- Review prerequisite warnings and install anything still missing before building the target app.', '- Run trine add button when you want to deliver the first Trine component into this project.', '- Build the target app and confirm @trine/ui resolves through the local consumer-owned source tree.');
248
+ lines.push('', 'Manual next steps:', '- Review prerequisite warnings and install anything still missing before building the target app.', '- Run trine add button when you want to deliver the first Trine component into this project.', '- Build the target app and confirm @trine/ui resolves through the local consumer-owned source tree.', '- If you want token-level customization, add semantic variable overrides below the Trine-managed block in the global stylesheet instead of editing inside the managed block.');
110
249
  console.log(lines.join('\n'));
111
250
  }
112
251
  function printAddSuccess(component, result, selection) {
@@ -121,7 +260,7 @@ function printAddSuccess(component, result, selection) {
121
260
  if (result.warnings.length > 0) {
122
261
  lines.push('', 'Warnings:', ...result.warnings.map((warning) => `- ${warning}`));
123
262
  }
124
- 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.`, '- For future setup in this repo, prefer running trine init first so project-level Trine baseline files are already in place.');
263
+ lines.push('', 'Manual next steps:', `- Ensure the target repo has Tailwind CSS v4, PostCSS with @tailwindcss/postcss, 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 if you want component-level local customization.`, '- Add semantic token overrides below the Trine-managed global stylesheet block if you want app-level visual customization that survives rerunning init.', '- For future setup in this repo, prefer running trine init first so the project-level Trine baseline is already in place.');
125
264
  if (isRepoDemoVerification) {
126
265
  lines.push('- Open /validation-shell and review the CLI delivery proof section for the temporary demo verification path.');
127
266
  }
@@ -130,6 +269,16 @@ function printAddSuccess(component, result, selection) {
130
269
  function renderFileGroup(files, emptyMessage) {
131
270
  return files.length > 0 ? files.map((file) => `- ${file}`) : [emptyMessage];
132
271
  }
272
+ function renderPlannedInitActions(plan) {
273
+ return [
274
+ ...plan.createdFiles.map((file) => `- create ${file}`),
275
+ ...plan.updatedFiles.map((file) => `- update ${file}`),
276
+ ...plan.reusedFiles.map((file) => `- keep ${file}`),
277
+ ];
278
+ }
279
+ function hasInitMutations(plan) {
280
+ return plan.createdFiles.length > 0 || plan.updatedFiles.length > 0;
281
+ }
133
282
  function isSupportedComponent(value) {
134
283
  return SUPPORTED_COMPONENTS.some((component) => component === value);
135
284
  }
@@ -143,11 +292,8 @@ function toDisplayTarget(cwd, targetRoot) {
143
292
  function toDisplayFilePath(targetRoot, filePath) {
144
293
  return path.relative(targetRoot, filePath) || '.';
145
294
  }
146
- try {
147
- main(process.argv.slice(2));
148
- }
149
- catch (error) {
295
+ await main(process.argv.slice(2)).catch((error) => {
150
296
  const message = error instanceof Error ? error.message : String(error);
151
297
  console.error(message);
152
298
  process.exitCode = 1;
153
- }
299
+ });
package/dist/init.js CHANGED
@@ -1,17 +1,15 @@
1
1
  import path from 'node:path';
2
- import { ensureSharedStyleBaseline, ensureStylesImport, ensureTsconfigAlias, ensureUiRootBarrel, isDemoTarget, readTargetDependencyWarnings, resolveSupportedProjectTarget, toTargetRelativePath, } from "./project.js";
3
- export function initProject(options) {
2
+ import { ensureTrineStylesheetBaseline, ensureTsconfigAlias, ensureUiRootBarrel, inspectTrineStylesheetBaseline, inspectTsconfigAlias, inspectUiRootBarrel, isDemoTarget, readTargetDependencyWarnings, resolveSupportedProjectTarget, toTargetRelativePath, } from "./project.js";
3
+ export function planInitProject(options) {
4
4
  const targetRoot = path.resolve(options.cwd, options.target);
5
- const { framework, targetStylesEntry, targetTsconfig } = resolveSupportedProjectTarget('trine init', targetRoot);
5
+ const { framework, targetStylesEntry, targetTsconfig } = resolveSupportedProjectTarget('trine init', targetRoot, {
6
+ preferredStylesEntry: options.targetStylesEntry,
7
+ });
6
8
  const createdFiles = [];
7
9
  const updatedFiles = [];
8
10
  const reusedFiles = [];
9
11
  const warnings = [];
10
- const sharedStylesResult = ensureSharedStyleBaseline(targetRoot, 'Trine components');
11
- createdFiles.push(...sharedStylesResult.createdFiles);
12
- reusedFiles.push(...sharedStylesResult.reusedFiles);
13
- warnings.push(...sharedStylesResult.warnings);
14
- const uiRootResult = ensureUiRootBarrel(targetRoot);
12
+ const uiRootResult = inspectUiRootBarrel(targetRoot);
15
13
  if (uiRootResult.created) {
16
14
  createdFiles.push(uiRootResult.file);
17
15
  }
@@ -19,22 +17,23 @@ export function initProject(options) {
19
17
  reusedFiles.push(uiRootResult.file);
20
18
  }
21
19
  const tsconfigDisplayPath = toTargetRelativePath(targetRoot, targetTsconfig);
22
- if (ensureTsconfigAlias(targetTsconfig, targetRoot, options.cwd)) {
20
+ if (inspectTsconfigAlias(targetTsconfig, targetRoot, options.cwd).needsUpdate) {
23
21
  updatedFiles.push(tsconfigDisplayPath);
24
22
  }
25
23
  else {
26
24
  reusedFiles.push(tsconfigDisplayPath);
27
25
  }
28
26
  const stylesDisplayPath = toTargetRelativePath(targetRoot, targetStylesEntry);
29
- const stylesResult = ensureStylesImport(targetStylesEntry);
30
- if (stylesResult.updated) {
27
+ const stylesResult = inspectTrineStylesheetBaseline(targetRoot, targetStylesEntry);
28
+ if (stylesResult.needsUpdate) {
31
29
  updatedFiles.push(stylesDisplayPath);
32
30
  }
33
31
  else {
34
32
  reusedFiles.push(stylesDisplayPath);
35
33
  }
34
+ warnings.push(...stylesResult.warnings);
36
35
  if (stylesResult.authoringImportStillPresent) {
37
- warnings.push(`${stylesDisplayPath} still imports @trine/ui/styles/trine.css for a broader authoring baseline outside the local Trine consumer setup.`);
36
+ warnings.push(`${stylesDisplayPath} keeps the repo-local @trine/ui/styles/trine.css authoring entry so apps/demo can still validate non-localized components during local development.`);
38
37
  }
39
38
  if (isDemoTarget(targetRoot)) {
40
39
  warnings.push('apps/demo keeps a temporary @trine/ui/* bridge for non-localized components so the internal demo app can still build while local Trine source is verified.');
@@ -44,6 +43,7 @@ export function initProject(options) {
44
43
  }
45
44
  return {
46
45
  targetRoot,
46
+ targetTsconfig,
47
47
  framework,
48
48
  targetStylesEntry,
49
49
  createdFiles,
@@ -52,3 +52,14 @@ export function initProject(options) {
52
52
  warnings,
53
53
  };
54
54
  }
55
+ export function applyInitPlan(plan, options) {
56
+ ensureUiRootBarrel(plan.targetRoot);
57
+ ensureTsconfigAlias(plan.targetTsconfig, plan.targetRoot, options.cwd);
58
+ ensureTrineStylesheetBaseline(plan.targetRoot, plan.targetStylesEntry);
59
+ return plan;
60
+ }
61
+ export function initProject(options) {
62
+ return applyInitPlan(planInitProject(options), {
63
+ cwd: options.cwd,
64
+ });
65
+ }