@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 +2 -1
- package/dist/add-component.js +10 -8
- package/dist/index.js +53 -7
- package/package.json +2 -2
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,
|
|
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`
|
package/dist/add-component.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
269
|
-
return
|
|
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
|
|
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) ??
|
|
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 (
|
|
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.
|
|
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": "
|
|
8
|
+
"trine": "bin/trine.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc -p tsconfig.build.json",
|