sitevision-cli 1.0.0-beta.31 → 1.0.0-beta.32
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/CHANGELOG.md +20 -0
- package/dist/cli.js +20 -2
- package/dist/scaffold/inquirer-stub.d.ts +6 -0
- package/dist/scaffold/inquirer-stub.js +119 -0
- package/dist/scaffold/register.d.ts +1 -0
- package/dist/scaffold/register.js +15 -0
- package/dist/shell/ConfigForm.d.ts +7 -1
- package/dist/shell/ConfigForm.js +95 -11
- package/dist/shell/Shell.d.ts +2 -1
- package/dist/shell/Shell.js +245 -10
- package/dist/shell/actions.d.ts +1 -0
- package/dist/shell/actions.js +0 -0
- package/dist/utils/create-app.d.ts +53 -0
- package/dist/utils/create-app.js +219 -0
- package/dist/utils/i18n.js +32 -0
- package/dist/utils/process-runner.d.ts +2 -1
- package/dist/utils/process-runner.js +4 -1
- package/dist/utils/project-detection.d.ts +10 -2
- package/dist/utils/project-detection.js +88 -11
- package/dist/utils/tasks.d.ts +12 -1
- package/dist/utils/tasks.js +2 -2
- package/package.json +1 -1
|
@@ -24,7 +24,8 @@ export declare class ProcessRunner extends EventEmitter {
|
|
|
24
24
|
private readonly command;
|
|
25
25
|
private readonly args;
|
|
26
26
|
private readonly cwd?;
|
|
27
|
-
|
|
27
|
+
private readonly env?;
|
|
28
|
+
constructor(command: string, args?: string[], cwd?: string, env?: NodeJS.ProcessEnv);
|
|
28
29
|
run(): Promise<ProcessResult>;
|
|
29
30
|
kill(): void;
|
|
30
31
|
getOutput(): ProcessOutput[];
|
|
@@ -53,16 +53,19 @@ export class ProcessRunner extends EventEmitter {
|
|
|
53
53
|
command;
|
|
54
54
|
args;
|
|
55
55
|
cwd;
|
|
56
|
-
|
|
56
|
+
env;
|
|
57
|
+
constructor(command, args = [], cwd, env) {
|
|
57
58
|
super();
|
|
58
59
|
this.command = command;
|
|
59
60
|
this.args = args;
|
|
60
61
|
this.cwd = cwd;
|
|
62
|
+
this.env = env;
|
|
61
63
|
}
|
|
62
64
|
run() {
|
|
63
65
|
return new Promise((resolve, reject) => {
|
|
64
66
|
this.process = spawnChild(this.command, this.args, {
|
|
65
67
|
cwd: this.cwd || process.cwd(),
|
|
68
|
+
env: this.env,
|
|
66
69
|
shell: true,
|
|
67
70
|
stdio: 'pipe',
|
|
68
71
|
});
|
|
@@ -99,6 +99,13 @@ export declare function readManifest(cwd: string): {
|
|
|
99
99
|
manifestPath: string;
|
|
100
100
|
manifest: SitevisionManifest;
|
|
101
101
|
} | null;
|
|
102
|
+
/**
|
|
103
|
+
* Set one text field of manifest.json, or one language of a localized field.
|
|
104
|
+
* An empty value removes the key. A value that is already a string is replaced
|
|
105
|
+
* in the text, so comments and formatting survive; anything else rewrites the
|
|
106
|
+
* file, which is refused when that would drop comments.
|
|
107
|
+
*/
|
|
108
|
+
export declare function writeManifestField(manifestPath: string, key: string, value: string, lang?: string): void;
|
|
102
109
|
/**
|
|
103
110
|
* Detect if the current directory is a Sitevision project
|
|
104
111
|
*/
|
|
@@ -169,8 +176,9 @@ export declare const IMPLICIT_VALUES: Record<string, unknown>;
|
|
|
169
176
|
export declare function getPackageJsonSyncChanges(dir: string): PackageJsonSyncChange[];
|
|
170
177
|
export declare function hasPackageJson(dir: string): boolean;
|
|
171
178
|
/**
|
|
172
|
-
* Copy the pending shared values from .dev_properties.json
|
|
173
|
-
*
|
|
179
|
+
* Copy the pending shared values from .dev_properties.json, and the manifest
|
|
180
|
+
* fields package.json mirrors, into package.json, creating package.json when
|
|
181
|
+
* the directory has none. Throws if it cannot be
|
|
174
182
|
* read or written.
|
|
175
183
|
*/
|
|
176
184
|
export declare function syncDevPropertiesToPackageJson(dir: string): boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { getDeployPassword, setDeployPassword, getSessionCookie, } from './keychain.js';
|
|
4
|
-
import { parseJsonc } from './jsonc.js';
|
|
4
|
+
import { parseJsonc, stripJsonComments } from './jsonc.js';
|
|
5
5
|
import { getLanguage } from './i18n.js';
|
|
6
6
|
// =============================================================================
|
|
7
7
|
// LOCALIZED TEXT
|
|
@@ -302,6 +302,40 @@ export function readManifest(cwd) {
|
|
|
302
302
|
}
|
|
303
303
|
return null;
|
|
304
304
|
}
|
|
305
|
+
const JSON_STRING = String.raw `"(?:[^"\\]|\\.)*"`;
|
|
306
|
+
const escapeRegExp = (s) => s.replaceAll(/[$()*+.?[\\\]^{|}]/g, String.raw `\$&`);
|
|
307
|
+
/**
|
|
308
|
+
* Set one text field of manifest.json, or one language of a localized field.
|
|
309
|
+
* An empty value removes the key. A value that is already a string is replaced
|
|
310
|
+
* in the text, so comments and formatting survive; anything else rewrites the
|
|
311
|
+
* file, which is refused when that would drop comments.
|
|
312
|
+
*/
|
|
313
|
+
export function writeManifestField(manifestPath, key, value, lang) {
|
|
314
|
+
const raw = fs.readFileSync(manifestPath, 'utf-8');
|
|
315
|
+
const wanted = parseJsonc(raw);
|
|
316
|
+
const holder = lang ? wanted[key] : wanted;
|
|
317
|
+
const leaf = lang ?? key;
|
|
318
|
+
// JSON.stringify drops an undefined key.
|
|
319
|
+
holder[leaf] = value || undefined;
|
|
320
|
+
const prefix = lang
|
|
321
|
+
? String.raw `"${escapeRegExp(key)}"\s*:\s*\{[^}]*?"${escapeRegExp(lang)}"\s*:\s*`
|
|
322
|
+
: String.raw `"${escapeRegExp(key)}"\s*:\s*`;
|
|
323
|
+
const patched = raw.replace(new RegExp(`(${prefix})${JSON_STRING}`), (_, before) => before + JSON.stringify(value));
|
|
324
|
+
let patchedOk = false;
|
|
325
|
+
try {
|
|
326
|
+
patchedOk = JSON.stringify(parseJsonc(patched)) === JSON.stringify(wanted);
|
|
327
|
+
}
|
|
328
|
+
catch { }
|
|
329
|
+
if (patchedOk) {
|
|
330
|
+
fs.writeFileSync(manifestPath, patched);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
if (stripJsonComments(raw) !== raw) {
|
|
334
|
+
throw new Error(`${manifestPath} has comments; add or remove "${leaf}" by hand`);
|
|
335
|
+
}
|
|
336
|
+
const indent = /^(?<indent>[\t ]+)/m.exec(raw)?.groups?.['indent'] ?? '\t';
|
|
337
|
+
fs.writeFileSync(manifestPath, JSON.stringify(wanted, null, indent) + (raw.endsWith('\n') ? '\n' : ''));
|
|
338
|
+
}
|
|
305
339
|
/**
|
|
306
340
|
* Detect if the current directory is a Sitevision project
|
|
307
341
|
*/
|
|
@@ -568,6 +602,38 @@ function pendingSync(dir) {
|
|
|
568
602
|
}
|
|
569
603
|
return pending;
|
|
570
604
|
}
|
|
605
|
+
// Manifest fields package.json mirrors under the same name; the manifest wins.
|
|
606
|
+
const MANIFEST_PACKAGE_KEYS = ['version', 'description', 'author'];
|
|
607
|
+
/**
|
|
608
|
+
* Manifest values the app's own package.json does not mirror yet. Nothing when
|
|
609
|
+
* either file is missing, so syncing never creates a package.json for this.
|
|
610
|
+
*/
|
|
611
|
+
function pendingManifestSync(dir) {
|
|
612
|
+
const packageJson = readPackageJson(dir);
|
|
613
|
+
if (!packageJson)
|
|
614
|
+
return [];
|
|
615
|
+
let manifest;
|
|
616
|
+
try {
|
|
617
|
+
manifest = readManifest(dir)?.manifest;
|
|
618
|
+
}
|
|
619
|
+
catch { }
|
|
620
|
+
if (!manifest)
|
|
621
|
+
return [];
|
|
622
|
+
const pending = [];
|
|
623
|
+
for (const key of MANIFEST_PACKAGE_KEYS) {
|
|
624
|
+
const value = key === 'description'
|
|
625
|
+
? localizedText(manifest.description, 'en')
|
|
626
|
+
: manifest[key];
|
|
627
|
+
const current = packageJson[key];
|
|
628
|
+
// ponytail: an author object ({name, email}) is left alone.
|
|
629
|
+
if (!value || typeof value !== 'string' || value === current)
|
|
630
|
+
continue;
|
|
631
|
+
if (current !== undefined && typeof current !== 'string')
|
|
632
|
+
continue;
|
|
633
|
+
pending.push({ key, from: current, value });
|
|
634
|
+
}
|
|
635
|
+
return pending;
|
|
636
|
+
}
|
|
571
637
|
/** What an unset field means, so spelling out the default is not a change. */
|
|
572
638
|
export const IMPLICIT_VALUES = {
|
|
573
639
|
authMethod: 'basic',
|
|
@@ -579,27 +645,38 @@ const display = (value) => typeof value === 'string' ? value : JSON.stringify(va
|
|
|
579
645
|
/** What syncing this directory would change in its package.json. */
|
|
580
646
|
export function getPackageJsonSyncChanges(dir) {
|
|
581
647
|
const current = readPackageDefaults(dir);
|
|
582
|
-
return
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
648
|
+
return [
|
|
649
|
+
...pendingSync(dir).map(({ key, value }) => current[key] === undefined
|
|
650
|
+
? { key: packageLabel(key), to: display(value) }
|
|
651
|
+
: {
|
|
652
|
+
key: packageLabel(key),
|
|
653
|
+
from: display(current[key]),
|
|
654
|
+
to: display(value),
|
|
655
|
+
}),
|
|
656
|
+
...pendingManifestSync(dir).map(({ key, from, value }) => ({
|
|
657
|
+
key,
|
|
658
|
+
...(from !== undefined && { from }),
|
|
659
|
+
to: value,
|
|
660
|
+
})),
|
|
661
|
+
];
|
|
589
662
|
}
|
|
590
663
|
export function hasPackageJson(dir) {
|
|
591
664
|
return fs.existsSync(path.join(dir, 'package.json'));
|
|
592
665
|
}
|
|
593
666
|
/**
|
|
594
|
-
* Copy the pending shared values from .dev_properties.json
|
|
595
|
-
*
|
|
667
|
+
* Copy the pending shared values from .dev_properties.json, and the manifest
|
|
668
|
+
* fields package.json mirrors, into package.json, creating package.json when
|
|
669
|
+
* the directory has none. Throws if it cannot be
|
|
596
670
|
* read or written.
|
|
597
671
|
*/
|
|
598
672
|
export function syncDevPropertiesToPackageJson(dir) {
|
|
599
673
|
const pending = pendingSync(dir);
|
|
600
|
-
|
|
674
|
+
const mirrored = pendingManifestSync(dir);
|
|
675
|
+
if (pending.length + mirrored.length === 0 && hasPackageJson(dir))
|
|
601
676
|
return false;
|
|
602
677
|
updatePackageJson(dir, packageJson => {
|
|
678
|
+
for (const { key, value } of mirrored)
|
|
679
|
+
packageJson[key] = value;
|
|
603
680
|
for (const { key, value } of pending) {
|
|
604
681
|
const topKey = PACKAGE_TOP_KEYS[key];
|
|
605
682
|
if (topKey) {
|
package/dist/utils/tasks.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ProjectInfo, SigningCredentials, DeployConfig } from '../types/index.js';
|
|
2
|
-
export type TaskKind = 'dev' | 'watch' | 'build' | 'sign' | 'deploy' | 'activate' | 'install';
|
|
2
|
+
export type TaskKind = 'dev' | 'watch' | 'build' | 'sign' | 'deploy' | 'activate' | 'install' | 'create';
|
|
3
3
|
export type TaskStatus = 'running' | 'success' | 'error' | 'stopped';
|
|
4
4
|
export type LogLevel = 'info' | 'ok' | 'warn' | 'error';
|
|
5
5
|
export interface LogLine {
|
|
@@ -26,6 +26,16 @@ export declare function getTasks(): Task[];
|
|
|
26
26
|
export declare function useTasks(): Task[];
|
|
27
27
|
export declare function runningTasks(appRoot?: string): Task[];
|
|
28
28
|
export declare function clearFinished(): void;
|
|
29
|
+
type Log = (tag: string, text: string, level?: LogLevel) => void;
|
|
30
|
+
export declare function createTask(kind: TaskKind, project: {
|
|
31
|
+
root: string;
|
|
32
|
+
manifest: Pick<ProjectInfo['manifest'], 'name' | 'id'>;
|
|
33
|
+
}, label: string, stop?: () => void): {
|
|
34
|
+
task: Task;
|
|
35
|
+
log: Log;
|
|
36
|
+
finish: (status: TaskStatus, error?: string) => void;
|
|
37
|
+
};
|
|
38
|
+
export declare function setPhase(task: Task, phase: string): void;
|
|
29
39
|
export interface DeployOptions {
|
|
30
40
|
force?: boolean;
|
|
31
41
|
production?: boolean;
|
|
@@ -48,3 +58,4 @@ export interface DevOptions {
|
|
|
48
58
|
* deploy. Runs until `task.stop()`; the task stays in the registry meanwhile.
|
|
49
59
|
*/
|
|
50
60
|
export declare function startDev(project: ProjectInfo, options: DevOptions): Task;
|
|
61
|
+
export {};
|
package/dist/utils/tasks.js
CHANGED
|
@@ -32,7 +32,7 @@ export function clearFinished() {
|
|
|
32
32
|
tasks = tasks.filter(t => t.status === 'running');
|
|
33
33
|
notify();
|
|
34
34
|
}
|
|
35
|
-
function createTask(kind, project, label, stop = () => { }) {
|
|
35
|
+
export function createTask(kind, project, label, stop = () => { }) {
|
|
36
36
|
const task = {
|
|
37
37
|
id: nextId++,
|
|
38
38
|
kind,
|
|
@@ -69,7 +69,7 @@ function createTask(kind, project, label, stop = () => { }) {
|
|
|
69
69
|
};
|
|
70
70
|
return { task, log, finish };
|
|
71
71
|
}
|
|
72
|
-
function setPhase(task, phase) {
|
|
72
|
+
export function setPhase(task, phase) {
|
|
73
73
|
task.phase = phase;
|
|
74
74
|
notify();
|
|
75
75
|
}
|