create-ovok 0.1.0-dev.22 → 0.1.0-dev.28
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 +3 -0
- package/dist/adapters/npm-registry.js +15 -0
- package/dist/domain/version.js +20 -0
- package/dist/main.js +24 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,6 +24,9 @@ Flags answer their questions, and `--yes` takes the recommended setup for the re
|
|
|
24
24
|
Outside a terminal, such as in CI, nothing is asked: unanswered questions take the recommended
|
|
25
25
|
setup.
|
|
26
26
|
|
|
27
|
+
In a terminal it also asks npm for the latest create-ovok and, when there is a newer one, ends
|
|
28
|
+
with the version to run next time.
|
|
29
|
+
|
|
27
30
|
Inside the workspace:
|
|
28
31
|
|
|
29
32
|
```bash
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const REGISTRY = "https://registry.npmjs.org";
|
|
2
|
+
/** The version npm installs by default, or null when the registry does not answer in time. */
|
|
3
|
+
export async function latestVersion(packageName, signal) {
|
|
4
|
+
const url = `${REGISTRY}/-/package/${encodeURIComponent(packageName)}/dist-tags`;
|
|
5
|
+
try {
|
|
6
|
+
const response = await fetch(url, { signal });
|
|
7
|
+
if (!response.ok)
|
|
8
|
+
return null;
|
|
9
|
+
const { latest } = (await response.json());
|
|
10
|
+
return typeof latest === "string" ? latest : null;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const NUMERIC_ORDER = new Intl.Collator("en", { numeric: true });
|
|
2
|
+
/** Semver order: numbers compare as numbers, and a release outranks its own prereleases. */
|
|
3
|
+
export function isNewer(candidate, current) {
|
|
4
|
+
const [candidateCore, candidatePrerelease] = splitPrerelease(candidate);
|
|
5
|
+
const [currentCore, currentPrerelease] = splitPrerelease(current);
|
|
6
|
+
const coreOrder = NUMERIC_ORDER.compare(candidateCore, currentCore);
|
|
7
|
+
if (coreOrder !== 0)
|
|
8
|
+
return coreOrder > 0;
|
|
9
|
+
if (candidatePrerelease === "" || currentPrerelease === "") {
|
|
10
|
+
return candidatePrerelease === "" && currentPrerelease !== "";
|
|
11
|
+
}
|
|
12
|
+
return NUMERIC_ORDER.compare(candidatePrerelease, currentPrerelease) > 0;
|
|
13
|
+
}
|
|
14
|
+
function splitPrerelease(version) {
|
|
15
|
+
const [withoutBuild = ""] = version.split("+");
|
|
16
|
+
const dash = withoutBuild.indexOf("-");
|
|
17
|
+
return dash === -1
|
|
18
|
+
? [withoutBuild, ""]
|
|
19
|
+
: [withoutBuild.slice(0, dash), withoutBuild.slice(dash + 1)];
|
|
20
|
+
}
|
package/dist/main.js
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
|
-
import { parseArgs } from "node:util";
|
|
3
|
+
import { parseArgs, styleText } from "node:util";
|
|
4
4
|
import { cancel } from "@clack/prompts";
|
|
5
5
|
import { ClackPrompt } from "./adapters/clack-prompt.js";
|
|
6
6
|
import { FsProjectFiles } from "./adapters/fs-project-files.js";
|
|
7
|
+
import { latestVersion } from "./adapters/npm-registry.js";
|
|
7
8
|
import { dependenciesAt } from "./adapters/scaffold-dependencies.js";
|
|
8
9
|
import { addApp, addPackage } from "./application/add-app.js";
|
|
9
10
|
import { refreshAgentRules } from "./application/refresh-agent-rules.js";
|
|
10
11
|
import { CancelledError, DEFAULT_ANSWERS, RECOMMENDED, } from "./application/workspace-answers.js";
|
|
11
12
|
import { initialiseGit, install, scaffold, showNextSteps } from "./commands/create.js";
|
|
12
13
|
import { MOBILE_OSES, osFor, PLATFORMS, parseChoice, parsePackageName, STARTING_PLATFORMS, } from "./domain/app-spec.js";
|
|
14
|
+
import { isNewer } from "./domain/version.js";
|
|
13
15
|
const EXIT_FAILURE = 1;
|
|
14
16
|
const EXIT_CANCELLED = 130;
|
|
17
|
+
const UPDATE_CHECK_TIMEOUT_MS = 1_500;
|
|
15
18
|
const OPTIONS = {
|
|
16
19
|
platform: { type: "string" },
|
|
17
20
|
type: { type: "string" },
|
|
@@ -69,7 +72,7 @@ async function main(argv) {
|
|
|
69
72
|
if (values.help)
|
|
70
73
|
return console.log(USAGE);
|
|
71
74
|
if (values.version)
|
|
72
|
-
return console.log(await
|
|
75
|
+
return console.log((await ownManifest()).version);
|
|
73
76
|
const [first, ...rest] = positionals;
|
|
74
77
|
const subcommand = first === undefined ? undefined : SUBCOMMANDS[first];
|
|
75
78
|
if (subcommand === undefined)
|
|
@@ -77,14 +80,30 @@ async function main(argv) {
|
|
|
77
80
|
return subcommand(rest, values);
|
|
78
81
|
}
|
|
79
82
|
async function create(name, values) {
|
|
80
|
-
const
|
|
83
|
+
const manifest = await ownManifest();
|
|
84
|
+
const pendingNotice = updateNotice(manifest);
|
|
85
|
+
const workspace = await scaffold(setupFlagsOf(name, values), promptFor(values), manifest.version);
|
|
81
86
|
const installation = values["skip-install"] ? "skipped" : await install(workspace);
|
|
82
87
|
if (!values["disable-git"])
|
|
83
88
|
await initialiseGit(workspace);
|
|
84
89
|
showNextSteps(workspace, installation);
|
|
90
|
+
const notice = await pendingNotice;
|
|
91
|
+
if (notice !== null)
|
|
92
|
+
console.error(notice);
|
|
85
93
|
if (installation === "failed")
|
|
86
94
|
process.exitCode = EXIT_FAILURE;
|
|
87
95
|
}
|
|
96
|
+
/** Asks npm while the workspace is made, and only when someone is at a terminal to read the answer. */
|
|
97
|
+
async function updateNotice({ name, version }) {
|
|
98
|
+
if (!process.stderr.isTTY)
|
|
99
|
+
return null;
|
|
100
|
+
const latest = await latestVersion(name, AbortSignal.timeout(UPDATE_CHECK_TIMEOUT_MS));
|
|
101
|
+
return latest !== null && isNewer(latest, version) ? updateMessage(version, latest) : null;
|
|
102
|
+
}
|
|
103
|
+
function updateMessage(current, latest) {
|
|
104
|
+
const update = styleText("cyan", `pnpm create ovok@${latest}`);
|
|
105
|
+
return `${styleText("yellow", "Update available:")} ${current} → ${latest}. Next time run ${update}`;
|
|
106
|
+
}
|
|
88
107
|
function promptFor(values) {
|
|
89
108
|
const interactive = process.stdin.isTTY && process.stdout.isTTY && !values.yes;
|
|
90
109
|
return interactive ? new ClackPrompt() : DEFAULT_ANSWERS;
|
|
@@ -103,9 +122,9 @@ function setupFlagsOf(name, values) {
|
|
|
103
122
|
function typeOf(values) {
|
|
104
123
|
return values.type === undefined ? null : parseChoice(values.type, MOBILE_OSES, "--type");
|
|
105
124
|
}
|
|
106
|
-
async function
|
|
125
|
+
async function ownManifest() {
|
|
107
126
|
const manifest = await readFile(new URL("../package.json", import.meta.url), "utf8");
|
|
108
|
-
return JSON.parse(manifest)
|
|
127
|
+
return JSON.parse(manifest);
|
|
109
128
|
}
|
|
110
129
|
try {
|
|
111
130
|
await main(process.argv.slice(2));
|