neutrinos-cli 2.0.0-beta.1 → 2.0.0-beta.10
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/.configs/auth.json +1 -1
- package/.configs/preferences.json +1 -1
- package/README.md +52 -11
- package/dist/{src/bin → bin}/cli.js +62 -4
- package/dist/{src/commands → commands}/attribute.js +5 -5
- package/dist/{src/commands → commands}/deprecate.js +1 -1
- package/dist/commands/dev.js +15 -0
- package/dist/commands/doctor.js +69 -0
- package/dist/commands/migrate.js +120 -0
- package/dist/{src/commands → commands}/new-workspace.js +26 -20
- package/dist/{src/commands → commands}/publish.js +1 -1
- package/dist/commands/test.js +27 -0
- package/dist/types/session.js +1 -0
- package/dist/types/workspace.js +1 -0
- package/dist/{src/utils → utils}/attribute-utils.js +2 -6
- package/dist/utils/doctor-checks/auth-state.js +35 -0
- package/dist/utils/doctor-checks/cli-update-available.js +58 -0
- package/dist/utils/doctor-checks/component-entry-file.js +47 -0
- package/dist/utils/doctor-checks/get-packages-safe.js +16 -0
- package/dist/utils/doctor-checks/index.js +30 -0
- package/dist/utils/doctor-checks/local-cli-version.js +39 -0
- package/dist/utils/doctor-checks/lock-file-sync.js +59 -0
- package/dist/utils/doctor-checks/node-modules.js +24 -0
- package/dist/utils/doctor-checks/node-version.js +24 -0
- package/dist/utils/doctor-checks/package-alpha-block.js +76 -0
- package/dist/utils/doctor-checks/plugin-json.js +63 -0
- package/dist/utils/doctor-checks/plugins-server.js +28 -0
- package/dist/utils/doctor-checks/root-package-json.js +58 -0
- package/dist/utils/doctor-checks/storybook.js +26 -0
- package/dist/utils/doctor-checks/tsconfig.js +37 -0
- package/dist/utils/doctor-checks/vitest-config.js +16 -0
- package/dist/{src/utils → utils}/generate-component.js +4 -3
- package/dist/utils/local-cli.js +39 -0
- package/dist/utils/migrate-runner.js +109 -0
- package/dist/utils/migrations/2.0.0-beta.7.js +84 -0
- package/dist/utils/migrations/2.0.0-beta.9.js +219 -0
- package/dist/utils/migrations/index.js +14 -0
- package/dist/utils/migrations/version-compare.js +47 -0
- package/dist/{src/utils → utils}/path-utils.js +1 -0
- package/dist/utils/registry.js +24 -0
- package/dist/utils/spawn-cli.js +30 -0
- package/package.json +8 -9
- package/templates/component/.component.ts.hbs +2 -2
- package/templates/component/.stories.ts.hbs +59 -0
- package/templates/project/.storybook/decorators/event-logger.ts +153 -0
- package/templates/project/.storybook/main.ts +18 -0
- package/templates/project/.storybook/preview.ts +12 -0
- package/templates/project/.vscode/extensions.json +1 -4
- package/templates/project/tsconfig.json +3 -6
- package/templates/project/vitest.config.ts +22 -0
- package/dist/src/commands/dev.js +0 -10
- package/templates/component/.spec.ts.hbs +0 -15
- package/templates/project/index.html +0 -24
- package/templates/project/index.ts +0 -86
- /package/dist/{src/cli-auth → cli-auth}/auth.js +0 -0
- /package/dist/{src/cli-auth → cli-auth}/server.js +0 -0
- /package/dist/{src/cli-auth → cli-auth}/services/auth-utils.js +0 -0
- /package/dist/{src/commands → commands}/build.js +0 -0
- /package/dist/{src/commands → commands}/completion.js +0 -0
- /package/dist/{src/commands → commands}/generate.js +0 -0
- /package/dist/{src/commands → commands}/select-packages.js +0 -0
- /package/dist/{src/commands → commands}/serve.js +0 -0
- /package/dist/{src/types → types}/alpha-package.js +0 -0
- /package/dist/{src/types/marketplace.js → types/doctor.js} +0 -0
- /package/dist/{src/types/session.js → types/marketplace.js} +0 -0
- /package/dist/{src/types/workspace.js → types/migrate.js} +0 -0
- /package/dist/{src/utils → utils}/check-valid-ws.js +0 -0
- /package/dist/{src/utils → utils}/copy-utils.js +0 -0
- /package/dist/{src/utils → utils}/create-client.js +0 -0
- /package/dist/{src/utils → utils}/file-utils.js +0 -0
- /package/dist/{src/utils → utils}/generate-module.js +0 -0
- /package/dist/{src/utils → utils}/get-package-info.js +0 -0
- /package/dist/{src/utils → utils}/get-packages.js +0 -0
- /package/dist/{src/utils → utils}/inquirer-utils.js +0 -0
- /package/dist/{src/utils → utils}/logger.js +0 -0
- /package/dist/{src/utils → utils}/marketplace-api-utils.js +0 -0
- /package/dist/{src/utils → utils}/prettify.js +0 -0
- /package/dist/{src/utils → utils}/user-session-utils.js +0 -0
package/.configs/auth.json
CHANGED
package/README.md
CHANGED
|
@@ -30,17 +30,19 @@ neutrinos start
|
|
|
30
30
|
neutrinos <command> [options]
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
| Command
|
|
34
|
-
|
|
35
|
-
| `new <name>`
|
|
36
|
-
| `generate\|g`
|
|
37
|
-
| `build <type> [name]` | Build plugins (`plugins` or `docker`)
|
|
38
|
-
| `start`
|
|
39
|
-
| `serve`
|
|
40
|
-
| `publish [name]`
|
|
41
|
-
| `deprecate [name]`
|
|
42
|
-
| `auth`
|
|
43
|
-
| `
|
|
33
|
+
| Command | Description |
|
|
34
|
+
| --------------------- | ----------------------------------------------------------------- |
|
|
35
|
+
| `new <name>` | Create a new plugin workspace |
|
|
36
|
+
| `generate\|g` | Generate a component, module, or attribute |
|
|
37
|
+
| `build <type> [name]` | Build plugins (`plugins` or `docker`) |
|
|
38
|
+
| `start` | Start the Vite dev server |
|
|
39
|
+
| `serve` | Start the Express plugins server |
|
|
40
|
+
| `publish [name]` | Bundle and publish a package |
|
|
41
|
+
| `deprecate [name]` | Deprecate a published package |
|
|
42
|
+
| `auth` | Login or check auth state |
|
|
43
|
+
| `doctor` | Check workspace health (`--fix` to auto-repair) |
|
|
44
|
+
| `migrate` | Migrate workspace to current CLI version (`--dry-run` to preview) |
|
|
45
|
+
| `completion <shell>` | Output shell completion script (`bash` or `zsh`) |
|
|
44
46
|
|
|
45
47
|
### `new <name>`
|
|
46
48
|
|
|
@@ -106,6 +108,45 @@ neutrinos deprecate [name] [-y] [--all]
|
|
|
106
108
|
|
|
107
109
|
Marks a published package as deprecated. Prompts for confirmation unless `-y` is passed.
|
|
108
110
|
|
|
111
|
+
### `doctor`
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
neutrinos doctor # Run all health checks
|
|
115
|
+
neutrinos doctor --fix # Auto-fix what it can
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Runs 14 workspace health checks and reports pass/warn/fail for each:
|
|
119
|
+
|
|
120
|
+
- `node_modules` — Dependencies installed
|
|
121
|
+
- `plugin.json` — Exists with required fields (`name`, `components.selectorPrefix`, `modules.idPrefix`)
|
|
122
|
+
- `package.json` — Has `workspaces`, `type: "module"`, expected dependencies
|
|
123
|
+
- `tsconfig.json` — Exists and is valid (verified via `tsc --showConfig`)
|
|
124
|
+
- `plugins-server/` — Directory exists with `index.js`
|
|
125
|
+
- `.storybook/` — Storybook config exists with `main.ts` and `preview.ts`
|
|
126
|
+
- `vitest.config.ts` — Vitest config exists
|
|
127
|
+
- Package `alpha` block — Each package has `alpha.component` or `alpha.module`
|
|
128
|
+
- Component entry file — Component packages have a matching `<name>.ts` file
|
|
129
|
+
- Node version — Meets `>=22` requirement
|
|
130
|
+
- Auth state — Token file exists and is not expired
|
|
131
|
+
- Lock file sync — Lock file is up to date with `package.json`
|
|
132
|
+
- CLI update available — Checks npm registry for newer versions
|
|
133
|
+
- Local CLI version — `devDependencies['neutrinos-cli']` matches running CLI
|
|
134
|
+
|
|
135
|
+
`--fix` auto-repairs: missing `node_modules` (runs install), `plugin.json` defaults, `workspaces`/`type` in `package.json`, `plugins-server/` from template, `.storybook/` from template, `vitest.config.ts` from template, missing `alpha` blocks, stale lock files.
|
|
136
|
+
|
|
137
|
+
### `migrate`
|
|
138
|
+
|
|
139
|
+
```sh
|
|
140
|
+
neutrinos migrate # Migrate workspace to current CLI version
|
|
141
|
+
neutrinos migrate --dry-run # Preview changes without writing
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Upgrades an existing workspace to match the current CLI version. Handles dependency updates, Storybook config scaffolding, file cleanup, and version pinning.
|
|
145
|
+
|
|
146
|
+
The migration enforces git safety: it errors on dirty working trees, auto-inits git if absent, and commits changes on success. All steps are idempotent — safe to re-run after `git checkout .`.
|
|
147
|
+
|
|
148
|
+
Version tracking uses `devDependencies['neutrinos-cli']` — no separate schema version field.
|
|
149
|
+
|
|
109
150
|
### `auth`
|
|
110
151
|
|
|
111
152
|
```sh
|
|
@@ -4,6 +4,29 @@ if (major < 22) {
|
|
|
4
4
|
console.error('neutrinos requires Node.js 22+. Current: ' + process.version);
|
|
5
5
|
process.exit(1);
|
|
6
6
|
}
|
|
7
|
+
// Delegate to workspace-local CLI binary if it exists.
|
|
8
|
+
// Only `new` always runs from the global binary (no workspace context to delegate to).
|
|
9
|
+
// Everything else delegates to local, matching Angular CLI's model.
|
|
10
|
+
import { resolveLocalCli, readLocalCliVersion } from '../utils/local-cli.js';
|
|
11
|
+
import { spawnCli } from '../utils/spawn-cli.js';
|
|
12
|
+
import { compareVersions } from '../utils/migrations/version-compare.js';
|
|
13
|
+
const GLOBAL_ONLY_COMMANDS = new Set(['new']);
|
|
14
|
+
const command = process.argv[2] ?? '';
|
|
15
|
+
if (!GLOBAL_ONLY_COMMANDS.has(command)) {
|
|
16
|
+
const localScript = resolveLocalCli(process.cwd(), realpathSync(fileURLToPath(import.meta.url)));
|
|
17
|
+
if (localScript) {
|
|
18
|
+
const globalVersion = JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf-8')).version;
|
|
19
|
+
const localVersion = readLocalCliVersion(process.cwd());
|
|
20
|
+
if (localVersion && compareVersions(globalVersion, localVersion) > 0) {
|
|
21
|
+
process.stderr.write(`\nWarning: global neutrinos CLI v${globalVersion} is newer than local v${localVersion}.\n` +
|
|
22
|
+
`Run "neutrinos migrate" to update this workspace.\n\n`);
|
|
23
|
+
}
|
|
24
|
+
const code = await spawnCli(localScript, process.argv.slice(2), {
|
|
25
|
+
env: { NEUTRINOS_GLOBAL_VERSION: globalVersion },
|
|
26
|
+
});
|
|
27
|
+
process.exit(code);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
7
30
|
import dotenv from 'dotenv';
|
|
8
31
|
import { PACKAGE_ROOT } from '../utils/path-utils.js';
|
|
9
32
|
dotenv.config({
|
|
@@ -26,15 +49,22 @@ import { generate } from '../commands/generate.js';
|
|
|
26
49
|
import { createWorkspace } from '../commands/new-workspace.js';
|
|
27
50
|
import { publish } from '../commands/publish.js';
|
|
28
51
|
import { startPluginsServer } from '../commands/serve.js';
|
|
52
|
+
import { runTests } from '../commands/test.js';
|
|
29
53
|
import { completion } from '../commands/completion.js';
|
|
54
|
+
import { doctor } from '../commands/doctor.js';
|
|
55
|
+
import { migrate } from '../commands/migrate.js';
|
|
30
56
|
import { getPackages } from '../utils/get-packages.js';
|
|
31
57
|
import { validateWorkspace } from '../utils/check-valid-ws.js';
|
|
32
58
|
import { done, failed, inprogress, log } from '../utils/logger.js';
|
|
33
|
-
import { authConfigJson } from '../utils/path-utils.js';
|
|
34
|
-
const { version: CLI_VERSION } = JSON.parse(readFileSync(
|
|
59
|
+
import { authConfigJson, getCliPackagePath } from '../utils/path-utils.js';
|
|
60
|
+
const { version: CLI_VERSION } = JSON.parse(readFileSync(getCliPackagePath(), 'utf-8'));
|
|
35
61
|
export const createProgram = () => {
|
|
36
62
|
const program = new Command();
|
|
37
|
-
|
|
63
|
+
const globalVersion = env.NEUTRINOS_GLOBAL_VERSION;
|
|
64
|
+
const versionString = globalVersion
|
|
65
|
+
? `${CLI_VERSION} (local)\n${globalVersion} (global)`
|
|
66
|
+
: `${CLI_VERSION} (global)`;
|
|
67
|
+
program.version(versionString, '-v, --version', 'Output the version number');
|
|
38
68
|
program
|
|
39
69
|
.command('new <name>')
|
|
40
70
|
.description('Create a new project')
|
|
@@ -178,6 +208,14 @@ export const createProgram = () => {
|
|
|
178
208
|
.action((options) => {
|
|
179
209
|
servePlugin(cwd(), Number(options.port) || 6969);
|
|
180
210
|
});
|
|
211
|
+
program
|
|
212
|
+
.command('test')
|
|
213
|
+
.alias('t')
|
|
214
|
+
.option('-w, --watch', 'Run in watch mode')
|
|
215
|
+
.description('run story-based tests')
|
|
216
|
+
.action((options) => {
|
|
217
|
+
runTests(cwd(), options);
|
|
218
|
+
});
|
|
181
219
|
program
|
|
182
220
|
.command('serve')
|
|
183
221
|
.option('-p, --port <port>', 'Port number for serving the plugins', '3000')
|
|
@@ -192,6 +230,21 @@ export const createProgram = () => {
|
|
|
192
230
|
.action((shell) => {
|
|
193
231
|
completion(program, shell);
|
|
194
232
|
});
|
|
233
|
+
program
|
|
234
|
+
.command('doctor')
|
|
235
|
+
.description('Check workspace health')
|
|
236
|
+
.option('--fix', 'Auto-fix issues where possible')
|
|
237
|
+
.action((options) => {
|
|
238
|
+
doctor(cwd(), options);
|
|
239
|
+
});
|
|
240
|
+
program
|
|
241
|
+
.command('migrate')
|
|
242
|
+
.description('Migrate workspace to a target CLI version')
|
|
243
|
+
.argument('[version]', 'Target version to migrate to')
|
|
244
|
+
.option('--dry-run', 'Preview changes without writing')
|
|
245
|
+
.action(async (version, options) => {
|
|
246
|
+
await migrate(cwd(), { ...options, version });
|
|
247
|
+
});
|
|
195
248
|
program
|
|
196
249
|
.command('__list-packages', { hidden: true })
|
|
197
250
|
.description('List workspace package names (used by shell completion)')
|
|
@@ -209,7 +262,12 @@ export const createProgram = () => {
|
|
|
209
262
|
});
|
|
210
263
|
program.hook('preAction', async (_thisCmd, actionCmd) => {
|
|
211
264
|
const cmd = actionCmd.name();
|
|
212
|
-
if (cmd === 'new' ||
|
|
265
|
+
if (cmd === 'new' ||
|
|
266
|
+
cmd === 'login' ||
|
|
267
|
+
cmd === 'completion' ||
|
|
268
|
+
cmd === '__list-packages' ||
|
|
269
|
+
cmd === 'doctor' ||
|
|
270
|
+
cmd === 'migrate') {
|
|
213
271
|
return;
|
|
214
272
|
}
|
|
215
273
|
if (!validateWorkspace(cwd())) {
|
|
@@ -65,9 +65,7 @@ const OPTIONS_QUERY = {
|
|
|
65
65
|
const getAttributeMetadata = async (options) => {
|
|
66
66
|
const attrMetadata = {
|
|
67
67
|
...options,
|
|
68
|
-
type: options.type && options.type in ATTR_TYPES
|
|
69
|
-
? ATTR_TYPES[options.type]
|
|
70
|
-
: '',
|
|
68
|
+
type: options.type && options.type in ATTR_TYPES ? ATTR_TYPES[options.type] : '',
|
|
71
69
|
};
|
|
72
70
|
if (!attrMetadata.type) {
|
|
73
71
|
const result = (await inquiry({
|
|
@@ -97,9 +95,11 @@ const getAttributeMetadata = async (options) => {
|
|
|
97
95
|
const data = {
|
|
98
96
|
uiType: UI_TYPES[uiType],
|
|
99
97
|
label: attrMetadata.label ?? (await inquiry(LABEL_QUERY)).label,
|
|
100
|
-
placeholder: attrMetadata.placeholder ??
|
|
98
|
+
placeholder: attrMetadata.placeholder ??
|
|
99
|
+
(await inquiry(PLACEHOLDER_QUERY)).placeholder,
|
|
101
100
|
category: attrMetadata.category ?? (await inquiry(CATEGORY_QUERY)).category,
|
|
102
|
-
defaultValue: attrMetadata.defaultValue ??
|
|
101
|
+
defaultValue: attrMetadata.defaultValue ??
|
|
102
|
+
(await inquiry(DEFAULT_VALUE_QUERY)).defaultValue,
|
|
103
103
|
};
|
|
104
104
|
Object.assign(attrMetadata, data);
|
|
105
105
|
const fieldMappings = FIELD_MAPPING_QUERY[attrMetadata.uiType];
|
|
@@ -12,7 +12,7 @@ const CONFIRM_INQUIRY = (packageName) => ({
|
|
|
12
12
|
default: false,
|
|
13
13
|
});
|
|
14
14
|
export const deprecate = async (pkgName, wsPath, options) => {
|
|
15
|
-
const session = await getLoggedInUserSession();
|
|
15
|
+
const session = (await getLoggedInUserSession());
|
|
16
16
|
const packages = await selectPackages(wsPath, options.all ?? false, pkgName);
|
|
17
17
|
await Promise.all(packages.map(async (componentDirPath) => {
|
|
18
18
|
try {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
export const servePlugin = async (wsPath, port) => {
|
|
3
|
+
const child = spawn('npx', ['storybook', 'dev', '-p', String(port)], {
|
|
4
|
+
cwd: wsPath,
|
|
5
|
+
stdio: 'inherit',
|
|
6
|
+
});
|
|
7
|
+
await new Promise((resolve, reject) => {
|
|
8
|
+
child.on('close', (code) => {
|
|
9
|
+
if (code === 0)
|
|
10
|
+
resolve();
|
|
11
|
+
else
|
|
12
|
+
reject(new Error(`Storybook exited with code ${code}`));
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { bold, greenBright, red, yellowBright } from 'colorette';
|
|
2
|
+
import { log as _log } from 'node:console';
|
|
3
|
+
import { checks } from '../utils/doctor-checks/index.js';
|
|
4
|
+
const SYMBOLS = {
|
|
5
|
+
pass: greenBright('✔'),
|
|
6
|
+
warn: yellowBright('⚠'),
|
|
7
|
+
fail: red('✖'),
|
|
8
|
+
};
|
|
9
|
+
const colorize = {
|
|
10
|
+
pass: greenBright,
|
|
11
|
+
warn: yellowBright,
|
|
12
|
+
fail: red,
|
|
13
|
+
};
|
|
14
|
+
function printResult(result) {
|
|
15
|
+
const sym = SYMBOLS[result.status];
|
|
16
|
+
const color = colorize[result.status];
|
|
17
|
+
_log(` ${sym} ${color(`${result.name} — ${result.message}`)}`);
|
|
18
|
+
}
|
|
19
|
+
function printFixedResult(before, after) {
|
|
20
|
+
_log(` ${red('✖')} → ${greenBright('✔')} ${greenBright(`${after.name} — Fixed: ${after.message}`)}`);
|
|
21
|
+
}
|
|
22
|
+
export function doctor(wsPath, opts) {
|
|
23
|
+
_log('\nWorkspace Health Check');
|
|
24
|
+
_log('──────────────────────\n');
|
|
25
|
+
let results = [];
|
|
26
|
+
for (const check of checks) {
|
|
27
|
+
const checkResults = check.run(wsPath);
|
|
28
|
+
results.push(...checkResults);
|
|
29
|
+
}
|
|
30
|
+
for (const r of results) {
|
|
31
|
+
printResult(r);
|
|
32
|
+
}
|
|
33
|
+
if (opts.fix) {
|
|
34
|
+
const fixableFailures = results.filter((r) => r.status === 'fail' && r.fixable);
|
|
35
|
+
if (fixableFailures.length > 0) {
|
|
36
|
+
_log('');
|
|
37
|
+
for (const check of checks) {
|
|
38
|
+
if (!check.fix)
|
|
39
|
+
continue;
|
|
40
|
+
const checkResults = check.run(wsPath);
|
|
41
|
+
const hasFixableFailure = checkResults.some((r) => r.status === 'fail' && r.fixable);
|
|
42
|
+
if (!hasFixableFailure)
|
|
43
|
+
continue;
|
|
44
|
+
check.fix(wsPath);
|
|
45
|
+
const afterResults = check.run(wsPath);
|
|
46
|
+
for (let i = 0; i < checkResults.length; i++) {
|
|
47
|
+
const before = checkResults[i];
|
|
48
|
+
const after = afterResults[i];
|
|
49
|
+
if (before.status === 'fail' && after && after.status === 'pass') {
|
|
50
|
+
printFixedResult(before, after);
|
|
51
|
+
const idx = results.findIndex((r) => r.name === before.name && r.status === 'fail');
|
|
52
|
+
if (idx !== -1) {
|
|
53
|
+
results[idx] = after;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const passed = results.filter((r) => r.status === 'pass').length;
|
|
61
|
+
const warned = results.filter((r) => r.status === 'warn').length;
|
|
62
|
+
const failed = results.filter((r) => r.status === 'fail').length;
|
|
63
|
+
_log('\n──────────────────────');
|
|
64
|
+
_log(bold(`Summary: ${greenBright(`${passed} passed`)}, ${yellowBright(`${warned} warnings`)}, ${red(`${failed} failed`)}`));
|
|
65
|
+
_log('');
|
|
66
|
+
if (failed > 0) {
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// src/commands/migrate.ts
|
|
2
|
+
import { exec as execCb } from 'node:child_process';
|
|
3
|
+
import { existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { promisify } from 'node:util';
|
|
7
|
+
import { greenBright } from 'colorette';
|
|
8
|
+
import { log as _log } from 'node:console';
|
|
9
|
+
import ora from 'ora';
|
|
10
|
+
import inquirer from 'inquirer';
|
|
11
|
+
import { readLocalCliVersion } from '../utils/local-cli.js';
|
|
12
|
+
import { compareVersions } from '../utils/migrations/version-compare.js';
|
|
13
|
+
import { fetchRegistryVersions, versionExists } from '../utils/registry.js';
|
|
14
|
+
import { spawnCli } from '../utils/spawn-cli.js';
|
|
15
|
+
import { runMigration } from '../utils/migrate-runner.js';
|
|
16
|
+
import { failed } from '../utils/logger.js';
|
|
17
|
+
const exec = promisify(execCb);
|
|
18
|
+
/**
|
|
19
|
+
* Migrate workspace to a target CLI version.
|
|
20
|
+
*
|
|
21
|
+
* - If NEUTRINOS_MIGRATE_SPAWNED is set, runs migrations directly (subprocess mode).
|
|
22
|
+
* - If a version argument is provided, temp-installs that version and spawns it.
|
|
23
|
+
* - If no version argument, prompts the user to select from available versions.
|
|
24
|
+
*/
|
|
25
|
+
export async function migrate(wsPath, opts) {
|
|
26
|
+
// Subprocess mode — spawned by a parent orchestrator, just run migrations
|
|
27
|
+
if (process.env.NEUTRINOS_MIGRATE_SPAWNED === 'true') {
|
|
28
|
+
runMigration(wsPath, { dryRun: opts.dryRun });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// Resolve target version
|
|
32
|
+
const versions = await fetchRegistryVersions();
|
|
33
|
+
let target;
|
|
34
|
+
if (opts.version) {
|
|
35
|
+
if (!versionExists(opts.version, versions)) {
|
|
36
|
+
failed(`Version ${opts.version} not found on the npm registry.`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
target = opts.version;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
target = await promptForVersion(wsPath, versions);
|
|
43
|
+
}
|
|
44
|
+
// Validate: refuse downgrades
|
|
45
|
+
const wsVersion = readLocalCliVersion(wsPath);
|
|
46
|
+
if (wsVersion && compareVersions(target, wsVersion) < 0) {
|
|
47
|
+
failed(`Cannot downgrade from v${wsVersion} to v${target}`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
// Temp install and spawn
|
|
51
|
+
const tmpDir = mkdtempSync(join(tmpdir(), 'neutrinos-migrate-'));
|
|
52
|
+
let exitCode = 1;
|
|
53
|
+
try {
|
|
54
|
+
await installToTempDir(tmpDir, target);
|
|
55
|
+
const cliBin = join(tmpDir, 'node_modules', 'neutrinos-cli', 'dist', 'src', 'bin', 'cli.js');
|
|
56
|
+
if (!existsSync(cliBin)) {
|
|
57
|
+
failed(`Failed to locate CLI binary in fetched package. Expected: ${cliBin}`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
const args = ['migrate'];
|
|
61
|
+
if (opts.dryRun)
|
|
62
|
+
args.push('--dry-run');
|
|
63
|
+
exitCode = await spawnCli(cliBin, args, {
|
|
64
|
+
cwd: wsPath,
|
|
65
|
+
env: { NEUTRINOS_MIGRATE_SPAWNED: 'true' },
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
70
|
+
}
|
|
71
|
+
process.exit(exitCode);
|
|
72
|
+
}
|
|
73
|
+
async function promptForVersion(wsPath, versions) {
|
|
74
|
+
const wsVersion = readLocalCliVersion(wsPath);
|
|
75
|
+
// Filter to versions newer than workspace
|
|
76
|
+
let available = wsVersion
|
|
77
|
+
? versions.filter((v) => compareVersions(v, wsVersion) > 0)
|
|
78
|
+
: versions;
|
|
79
|
+
if (available.length === 0) {
|
|
80
|
+
_log(greenBright('\nAlready up to date.\n'));
|
|
81
|
+
process.exit(0);
|
|
82
|
+
}
|
|
83
|
+
// Sort descending (newest first)
|
|
84
|
+
available = available.sort((a, b) => compareVersions(b, a));
|
|
85
|
+
// Group: stable vs prerelease
|
|
86
|
+
const stable = available.filter((v) => !v.includes('-'));
|
|
87
|
+
const prerelease = available.filter((v) => v.includes('-'));
|
|
88
|
+
const choices = [];
|
|
89
|
+
if (stable.length > 0) {
|
|
90
|
+
choices.push(new inquirer.Separator('── Stable ──'));
|
|
91
|
+
for (const v of stable)
|
|
92
|
+
choices.push({ name: v, value: v });
|
|
93
|
+
}
|
|
94
|
+
if (prerelease.length > 0) {
|
|
95
|
+
choices.push(new inquirer.Separator('── Pre-release ──'));
|
|
96
|
+
for (const v of prerelease)
|
|
97
|
+
choices.push({ name: v, value: v });
|
|
98
|
+
}
|
|
99
|
+
const { version } = await inquirer.prompt([
|
|
100
|
+
{
|
|
101
|
+
type: 'list',
|
|
102
|
+
name: 'version',
|
|
103
|
+
message: 'Select target version:',
|
|
104
|
+
choices,
|
|
105
|
+
},
|
|
106
|
+
]);
|
|
107
|
+
return version;
|
|
108
|
+
}
|
|
109
|
+
async function installToTempDir(tmpDir, version) {
|
|
110
|
+
writeFileSync(join(tmpDir, 'package.json'), JSON.stringify({ dependencies: { 'neutrinos-cli': version } }));
|
|
111
|
+
const spinner = ora(`Fetching neutrinos-cli@${version}...`).start();
|
|
112
|
+
try {
|
|
113
|
+
await exec('npm install --ignore-scripts --no-package-lock', { cwd: tmpDir });
|
|
114
|
+
spinner.succeed(`Fetched neutrinos-cli@${version}`);
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
spinner.fail(`Failed to fetch neutrinos-cli@${version}`);
|
|
118
|
+
throw err;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -4,7 +4,7 @@ import { cpSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { exit } from 'node:process';
|
|
6
6
|
import { done, failed, inprogress } from '../utils/logger.js';
|
|
7
|
-
import { pluginJsonPath, pluginServerTemplatesPath, templatesPath } from '../utils/path-utils.js';
|
|
7
|
+
import { getCliPackagePath, pluginJsonPath, pluginServerTemplatesPath, templatesPath } from '../utils/path-utils.js';
|
|
8
8
|
import { prettify } from '../utils/prettify.js';
|
|
9
9
|
export const createWorkspace = async (dir, name) => {
|
|
10
10
|
process.on('SIGINT', () => {
|
|
@@ -74,31 +74,37 @@ const cleanUp = (dir) => {
|
|
|
74
74
|
rmSync(dir, { recursive: true });
|
|
75
75
|
done('Cleaned up workspace');
|
|
76
76
|
};
|
|
77
|
+
export const buildWorkspacePackageJson = (base, cliVersion) => ({
|
|
78
|
+
...base,
|
|
79
|
+
scripts: {},
|
|
80
|
+
devDependencies: {
|
|
81
|
+
'neutrinos-cli': cliVersion,
|
|
82
|
+
lit: '^3.3.2',
|
|
83
|
+
typescript: '^6.0.2',
|
|
84
|
+
storybook: '^10.3.1',
|
|
85
|
+
'@storybook/web-components-vite': '^10.3.1',
|
|
86
|
+
'@storybook/addon-vitest': '^10.3.1',
|
|
87
|
+
vitest: '^4.0.0',
|
|
88
|
+
'@vitest/browser': '^4.0.0',
|
|
89
|
+
'@vitest/browser-playwright': '^4.0.0',
|
|
90
|
+
},
|
|
91
|
+
dependencies: {
|
|
92
|
+
express: '^5.2.1',
|
|
93
|
+
'@jatahworx/alpha-annotations-lib': '^1.0.12',
|
|
94
|
+
},
|
|
95
|
+
workspaces: ['packages/*'],
|
|
96
|
+
type: 'module',
|
|
97
|
+
});
|
|
77
98
|
const initializePackages = async (dir) => {
|
|
78
|
-
inprogress('
|
|
79
|
-
const
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
start: 'neutrinos start',
|
|
83
|
-
serve: 'neutrinos serve',
|
|
84
|
-
};
|
|
85
|
-
pkgJson.devDependencies = {
|
|
86
|
-
lit: '^3.1.4',
|
|
87
|
-
typescript: '^5.2.2',
|
|
88
|
-
};
|
|
89
|
-
pkgJson.dependencies = {
|
|
90
|
-
express: '^4.19.2',
|
|
91
|
-
'@jatahworx/alpha-annotations-lib': '^1.0.9',
|
|
92
|
-
};
|
|
93
|
-
pkgJson.workspaces = ['packages/*'];
|
|
94
|
-
pkgJson.type = 'module';
|
|
95
|
-
Object.assign(pkgJson.scripts, {}, scripts);
|
|
99
|
+
inprogress('Installing default packages in workspace...');
|
|
100
|
+
const { version: cliVersion } = JSON.parse(readFileSync(getCliPackagePath(), 'utf-8'));
|
|
101
|
+
const base = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf-8'));
|
|
102
|
+
const pkgJson = buildWorkspacePackageJson(base, cliVersion);
|
|
96
103
|
writeFileSync(join(dir, 'package.json'), await prettify(pkgJson, 'json'));
|
|
97
104
|
execSync('npm install', {
|
|
98
105
|
cwd: dir,
|
|
99
106
|
});
|
|
100
107
|
done(`Installed default packages in workspace: ${bold(dir)}`);
|
|
101
|
-
done(`Added default npm scripts to workspace: ${bold(dir)}`);
|
|
102
108
|
};
|
|
103
109
|
const initializeStaticServer = (dir) => {
|
|
104
110
|
cpSync(pluginServerTemplatesPath(), join(dir, 'plugins-server'), {
|
|
@@ -26,7 +26,7 @@ const IMAGES_FILE_PATH_QUERY = {
|
|
|
26
26
|
message: 'Enter the path to images of the package :',
|
|
27
27
|
};
|
|
28
28
|
export const publish = async (name, wsPath, options) => {
|
|
29
|
-
const session = await getLoggedInUserSession();
|
|
29
|
+
const session = (await getLoggedInUserSession());
|
|
30
30
|
const { packageName, componentDirPath } = await getPackageMetadata(wsPath, name);
|
|
31
31
|
await handlePackagePublish(packageName, componentDirPath, wsPath, options, session);
|
|
32
32
|
done('Published Successfully');
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { failed } from '../utils/logger.js';
|
|
5
|
+
export const runTests = async (wsPath, options) => {
|
|
6
|
+
const vitestConfig = join(wsPath, 'vitest.config.ts');
|
|
7
|
+
if (!existsSync(vitestConfig)) {
|
|
8
|
+
failed('vitest.config.ts not found in workspace. Run "neutrinos doctor --fix" or create it manually.');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const args = ['vitest'];
|
|
12
|
+
if (!options.watch) {
|
|
13
|
+
args.push('--run');
|
|
14
|
+
}
|
|
15
|
+
const child = spawn('npx', args, {
|
|
16
|
+
cwd: wsPath,
|
|
17
|
+
stdio: 'inherit',
|
|
18
|
+
});
|
|
19
|
+
await new Promise((resolve, reject) => {
|
|
20
|
+
child.on('close', (code) => {
|
|
21
|
+
if (code === 0)
|
|
22
|
+
resolve();
|
|
23
|
+
else
|
|
24
|
+
reject(new Error(`Vitest exited with code ${code}`));
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -42,9 +42,7 @@ const addEventTypeDecorator = (classDeclaration, attrMetadata) => {
|
|
|
42
42
|
}
|
|
43
43
|
renderFunction.addDecorator({
|
|
44
44
|
name: 'AlphaAttribute',
|
|
45
|
-
arguments: [
|
|
46
|
-
(writer) => writeDecoratorArguments(writer, attrMetadata),
|
|
47
|
-
],
|
|
45
|
+
arguments: [(writer) => writeDecoratorArguments(writer, attrMetadata)],
|
|
48
46
|
});
|
|
49
47
|
};
|
|
50
48
|
const addPropertyDecorator = (classDeclaration, fieldMappings, attrMetadata) => {
|
|
@@ -68,9 +66,7 @@ const addPropertyDecorator = (classDeclaration, fieldMappings, attrMetadata) =>
|
|
|
68
66
|
}
|
|
69
67
|
property.addDecorator({
|
|
70
68
|
name: 'AlphaAttribute',
|
|
71
|
-
arguments: [
|
|
72
|
-
(writer) => writeDecoratorArguments(writer, attrMetadata),
|
|
73
|
-
],
|
|
69
|
+
arguments: [(writer) => writeDecoratorArguments(writer, attrMetadata)],
|
|
74
70
|
});
|
|
75
71
|
};
|
|
76
72
|
const writeDecoratorArguments = (writer, attrMetadata) => {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { authConfigJson } from '../path-utils.js';
|
|
3
|
+
export const authStateCheck = {
|
|
4
|
+
name: 'Auth state',
|
|
5
|
+
run() {
|
|
6
|
+
const authPath = authConfigJson();
|
|
7
|
+
if (!existsSync(authPath)) {
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
name: 'Auth state',
|
|
11
|
+
status: 'fail',
|
|
12
|
+
message: `${authPath} not found. Run "neutrinos auth login"`,
|
|
13
|
+
fixable: false,
|
|
14
|
+
},
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const tokenSet = JSON.parse(readFileSync(authPath, 'utf-8'));
|
|
19
|
+
if (tokenSet.expires_at && tokenSet.expires_at < Date.now() / 1000) {
|
|
20
|
+
return [
|
|
21
|
+
{
|
|
22
|
+
name: 'Auth state',
|
|
23
|
+
status: 'warn',
|
|
24
|
+
message: 'Token expired. Run "neutrinos auth login"',
|
|
25
|
+
fixable: false,
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return [{ name: 'Auth state', status: 'warn', message: 'Could not parse auth.json', fixable: false }];
|
|
32
|
+
}
|
|
33
|
+
return [{ name: 'Auth state', status: 'pass', message: 'Token present and valid', fixable: false }];
|
|
34
|
+
},
|
|
35
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { getCliPackagePath } from '../path-utils.js';
|
|
4
|
+
export const cliUpdateAvailableCheck = {
|
|
5
|
+
name: 'CLI update available',
|
|
6
|
+
run() {
|
|
7
|
+
const { version: current } = JSON.parse(readFileSync(getCliPackagePath(), 'utf-8'));
|
|
8
|
+
const isBeta = current.includes('-beta');
|
|
9
|
+
const tag = isBeta ? 'beta' : 'latest';
|
|
10
|
+
try {
|
|
11
|
+
const output = execSync('npm view neutrinos-cli dist-tags --json', {
|
|
12
|
+
stdio: 'pipe',
|
|
13
|
+
encoding: 'utf-8',
|
|
14
|
+
timeout: 3000,
|
|
15
|
+
});
|
|
16
|
+
const tags = JSON.parse(output);
|
|
17
|
+
const latest = tags[tag];
|
|
18
|
+
if (!latest) {
|
|
19
|
+
return [
|
|
20
|
+
{
|
|
21
|
+
name: 'CLI update available',
|
|
22
|
+
status: 'pass',
|
|
23
|
+
message: `v${current} (could not determine ${tag} tag)`,
|
|
24
|
+
fixable: false,
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
if (latest === current) {
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
name: 'CLI update available',
|
|
32
|
+
status: 'pass',
|
|
33
|
+
message: `v${current} is the latest ${tag}`,
|
|
34
|
+
fixable: false,
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
return [
|
|
39
|
+
{
|
|
40
|
+
name: 'CLI update available',
|
|
41
|
+
status: 'warn',
|
|
42
|
+
message: `v${current} → v${latest} available. Run "npm i -g neutrinos-cli@${tag}"`,
|
|
43
|
+
fixable: false,
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return [
|
|
49
|
+
{
|
|
50
|
+
name: 'CLI update available',
|
|
51
|
+
status: 'warn',
|
|
52
|
+
message: `v${current} (could not reach npm registry)`,
|
|
53
|
+
fixable: false,
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
};
|