adjacent-cli 0.1.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/CHANGELOG.md +15 -0
- package/LICENSE +21 -0
- package/README.md +48 -0
- package/dist/arguments.d.ts +54 -0
- package/dist/arguments.d.ts.map +1 -0
- package/dist/arguments.js +68 -0
- package/dist/arguments.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +140 -0
- package/dist/bin.js.map +1 -0
- package/dist/checks.d.ts +35 -0
- package/dist/checks.d.ts.map +1 -0
- package/dist/checks.js +206 -0
- package/dist/checks.js.map +1 -0
- package/dist/create.d.ts +42 -0
- package/dist/create.d.ts.map +1 -0
- package/dist/create.js +82 -0
- package/dist/create.js.map +1 -0
- package/dist/dependencies.d.ts +59 -0
- package/dist/dependencies.d.ts.map +1 -0
- package/dist/dependencies.js +100 -0
- package/dist/dependencies.js.map +1 -0
- package/dist/destination.d.ts +16 -0
- package/dist/destination.d.ts.map +1 -0
- package/dist/destination.js +39 -0
- package/dist/destination.js.map +1 -0
- package/dist/dev-channel.d.ts +40 -0
- package/dist/dev-channel.d.ts.map +1 -0
- package/dist/dev-channel.js +192 -0
- package/dist/dev-channel.js.map +1 -0
- package/dist/dev.d.ts +3 -0
- package/dist/dev.d.ts.map +1 -0
- package/dist/dev.js +12 -0
- package/dist/dev.js.map +1 -0
- package/dist/doctor.d.ts +46 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +52 -0
- package/dist/doctor.js.map +1 -0
- package/dist/edits.d.ts +26 -0
- package/dist/edits.d.ts.map +1 -0
- package/dist/edits.js +101 -0
- package/dist/edits.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +62 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +275 -0
- package/dist/init.js.map +1 -0
- package/dist/keys.d.ts +14 -0
- package/dist/keys.d.ts.map +1 -0
- package/dist/keys.js +66 -0
- package/dist/keys.js.map +1 -0
- package/dist/package-manager.d.ts +40 -0
- package/dist/package-manager.d.ts.map +1 -0
- package/dist/package-manager.js +98 -0
- package/dist/package-manager.js.map +1 -0
- package/dist/process.d.ts +33 -0
- package/dist/process.d.ts.map +1 -0
- package/dist/process.js +95 -0
- package/dist/process.js.map +1 -0
- package/dist/project-name.d.ts +30 -0
- package/dist/project-name.d.ts.map +1 -0
- package/dist/project-name.js +73 -0
- package/dist/project-name.js.map +1 -0
- package/dist/reporter.d.ts +12 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +11 -0
- package/dist/reporter.js.map +1 -0
- package/dist/rewrite.d.ts +54 -0
- package/dist/rewrite.d.ts.map +1 -0
- package/dist/rewrite.js +262 -0
- package/dist/rewrite.js.map +1 -0
- package/dist/shutdown.d.ts +24 -0
- package/dist/shutdown.d.ts.map +1 -0
- package/dist/shutdown.js +67 -0
- package/dist/shutdown.js.map +1 -0
- package/dist/survey.d.ts +33 -0
- package/dist/survey.d.ts.map +1 -0
- package/dist/survey.js +94 -0
- package/dist/survey.js.map +1 -0
- package/dist/template.d.ts +29 -0
- package/dist/template.d.ts.map +1 -0
- package/dist/template.js +77 -0
- package/dist/template.js.map +1 -0
- package/dist/ui.d.ts +14 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +50 -0
- package/dist/ui.js.map +1 -0
- package/package.json +65 -0
- package/templates/adjacent/app/manifest.ts +5 -0
- package/templates/adjacent/app/sw.js/route.ts +5 -0
- package/templates/adjacent/app.config.ts +21 -0
- package/templates/adjacent/components/navigation.tsx +46 -0
- package/templates/adjacent/public/apple-touch-icon.png +0 -0
- package/templates/adjacent/public/favicon-96x96.png +0 -0
- package/templates/adjacent/public/favicon.ico +0 -0
- package/templates/adjacent/public/favicon.svg +4 -0
- package/templates/adjacent/public/web-app-manifest-192x192.png +0 -0
- package/templates/adjacent/public/web-app-manifest-512x512.png +0 -0
- package/templates/adjacent/public/web-app-manifest-maskable-192x192.png +0 -0
- package/templates/adjacent/public/web-app-manifest-maskable-512x512.png +0 -0
- package/templates/starter/app/(app)/layout.tsx +12 -0
- package/templates/starter/app/(app)/page.tsx +75 -0
- package/templates/starter/app/(app)/settings/page.tsx +24 -0
- package/templates/starter/app/(app)/settings/settings-panel.tsx +54 -0
package/dist/shutdown.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export const FORCE_EXIT_MILLISECONDS = 5_000;
|
|
2
|
+
const SHUTDOWN_SIGNALS = [
|
|
3
|
+
"SIGHUP",
|
|
4
|
+
"SIGINT",
|
|
5
|
+
"SIGTERM",
|
|
6
|
+
];
|
|
7
|
+
/** Bun types `process.removeListener` too narrowly to pass a signal name. */
|
|
8
|
+
const lifecycle = process;
|
|
9
|
+
/**
|
|
10
|
+
* A Clack spinner registers a SIGINT listener that only draws a cancel message,
|
|
11
|
+
* which also suppresses Node's default terminate-on-SIGINT. Quitting therefore
|
|
12
|
+
* has to exit explicitly, survive a teardown step that never settles, and stay
|
|
13
|
+
* interruptible so a second interrupt is not silently ignored.
|
|
14
|
+
*/
|
|
15
|
+
export function createShutdown(options) {
|
|
16
|
+
const { exit, forceMilliseconds = FORCE_EXIT_MILLISECONDS, kill, notify, release: releaseResources, restore, } = options;
|
|
17
|
+
let releasing = false;
|
|
18
|
+
let forced = false;
|
|
19
|
+
const force = (code) => {
|
|
20
|
+
if (forced)
|
|
21
|
+
return;
|
|
22
|
+
forced = true;
|
|
23
|
+
restore();
|
|
24
|
+
kill();
|
|
25
|
+
exit(code);
|
|
26
|
+
};
|
|
27
|
+
const release = async () => {
|
|
28
|
+
if (releasing)
|
|
29
|
+
return;
|
|
30
|
+
releasing = true;
|
|
31
|
+
restore();
|
|
32
|
+
await releaseResources();
|
|
33
|
+
};
|
|
34
|
+
const request = (code = 130) => {
|
|
35
|
+
if (releasing) {
|
|
36
|
+
force(code);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
notify?.("Stopping. Press Ctrl-C again to force.");
|
|
40
|
+
const deadline = setTimeout(() => force(code), forceMilliseconds);
|
|
41
|
+
void release().then(() => {
|
|
42
|
+
clearTimeout(deadline);
|
|
43
|
+
if (forced)
|
|
44
|
+
return;
|
|
45
|
+
forced = true;
|
|
46
|
+
kill();
|
|
47
|
+
exit(code);
|
|
48
|
+
}, () => {
|
|
49
|
+
clearTimeout(deadline);
|
|
50
|
+
force(1);
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
const listen = () => {
|
|
54
|
+
const onSignal = () => request(130);
|
|
55
|
+
const onExit = () => restore();
|
|
56
|
+
SHUTDOWN_SIGNALS.forEach((signal) => lifecycle.on(signal, onSignal));
|
|
57
|
+
lifecycle.once("exit", onExit);
|
|
58
|
+
return () => {
|
|
59
|
+
SHUTDOWN_SIGNALS.forEach((signal) => {
|
|
60
|
+
lifecycle.removeListener(signal, onSignal);
|
|
61
|
+
});
|
|
62
|
+
lifecycle.removeListener("exit", onExit);
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
return { listen, release, request };
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=shutdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shutdown.js","sourceRoot":"","sources":["../src/shutdown.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,uBAAuB,GAAG,KAAK,CAAC;AAE7C,MAAM,gBAAgB,GAA8B;IAClD,QAAQ;IACR,QAAQ;IACR,SAAS;CACV,CAAC;AAEF,6EAA6E;AAC7E,MAAM,SAAS,GAAG,OAIjB,CAAC;AAmBF;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAwB;IACrD,MAAM,EACJ,IAAI,EACJ,iBAAiB,GAAG,uBAAuB,EAC3C,IAAI,EACJ,MAAM,EACN,OAAO,EAAE,gBAAgB,EACzB,OAAO,GACR,GAAG,OAAO,CAAC;IAEZ,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,MAAM,KAAK,GAAG,CAAC,IAAY,EAAQ,EAAE;QACnC,IAAI,MAAM;YAAE,OAAO;QACnB,MAAM,GAAG,IAAI,CAAC;QACd,OAAO,EAAE,CAAC;QACV,IAAI,EAAE,CAAC;QACP,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,KAAK,IAAmB,EAAE;QACxC,IAAI,SAAS;YAAE,OAAO;QACtB,SAAS,GAAG,IAAI,CAAC;QACjB,OAAO,EAAE,CAAC;QACV,MAAM,gBAAgB,EAAE,CAAC;IAC3B,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,CAAC,IAAI,GAAG,GAAG,EAAQ,EAAE;QACnC,IAAI,SAAS,EAAE,CAAC;YACd,KAAK,CAAC,IAAI,CAAC,CAAC;YACZ,OAAO;QACT,CAAC;QAED,MAAM,EAAE,CAAC,wCAAwC,CAAC,CAAC;QACnD,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,iBAAiB,CAAC,CAAC;QAElE,KAAK,OAAO,EAAE,CAAC,IAAI,CACjB,GAAG,EAAE;YACH,YAAY,CAAC,QAAQ,CAAC,CAAC;YACvB,IAAI,MAAM;gBAAE,OAAO;YACnB,MAAM,GAAG,IAAI,CAAC;YACd,IAAI,EAAE,CAAC;YACP,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,CAAC,EACD,GAAG,EAAE;YACH,YAAY,CAAC,QAAQ,CAAC,CAAC;YACvB,KAAK,CAAC,CAAC,CAAC,CAAC;QACX,CAAC,CACF,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,GAAiB,EAAE;QAChC,MAAM,QAAQ,GAAG,GAAS,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,GAAS,EAAE,CAAC,OAAO,EAAE,CAAC;QAErC,gBAAgB,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;QACrE,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAE/B,OAAO,GAAG,EAAE;YACV,gBAAgB,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;gBAClC,SAAS,CAAC,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YAC7C,CAAC,CAAC,CAAC;YACH,SAAS,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC,CAAC;IACJ,CAAC,CAAC;IAEF,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AACtC,CAAC"}
|
package/dist/survey.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type ProjectIdentity } from "./project-name.js";
|
|
2
|
+
/** What Adjacent needs to know about an application before injecting anything. */
|
|
3
|
+
export interface ProjectSurvey {
|
|
4
|
+
/** Relative to the root: `app` or `src/app`. */
|
|
5
|
+
readonly appDirectory: string;
|
|
6
|
+
/** Relative path of the stylesheet the root layout imports, when there is one. */
|
|
7
|
+
readonly globalsCss?: string;
|
|
8
|
+
/** True once `app.config.ts` exists, which makes a second run a no-op. */
|
|
9
|
+
readonly hasAdjacentConfig: boolean;
|
|
10
|
+
readonly hasTailwind: boolean;
|
|
11
|
+
readonly identity: ProjectIdentity;
|
|
12
|
+
/** Relative path of the root layout. */
|
|
13
|
+
readonly layoutFile: string;
|
|
14
|
+
/** Relative path of the Next.js config, when the project has one. */
|
|
15
|
+
readonly nextConfigFile?: string;
|
|
16
|
+
readonly packageJson: Record<string, unknown>;
|
|
17
|
+
readonly root: string;
|
|
18
|
+
readonly typescript: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A project name has to survive being turned into an Adjacent slug. An existing
|
|
22
|
+
* application may be called anything at all, including a scoped name, so the
|
|
23
|
+
* unusable parts are dropped rather than refusing to work on the project.
|
|
24
|
+
*/
|
|
25
|
+
export declare function toProjectName(packageName: unknown, fallback: string): string;
|
|
26
|
+
export declare class NotANextApplicationError extends Error {
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Reads the shape of an existing Next.js application. Everything Adjacent later
|
|
30
|
+
* writes or rewrites is decided from this, so detection stays in one place.
|
|
31
|
+
*/
|
|
32
|
+
export declare function surveyProject(root: string): Promise<ProjectSurvey>;
|
|
33
|
+
//# sourceMappingURL=survey.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"survey.d.ts","sourceRoot":"","sources":["../src/survey.ts"],"names":[],"mappings":"AAGA,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEhF,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,kFAAkF;IAClF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,0EAA0E;IAC1E,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,wCAAwC;IACxC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,qEAAqE;IACrE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAwCD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAU5E;AAED,qBAAa,wBAAyB,SAAQ,KAAK;CAAG;AAEtD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA6DxE"}
|
package/dist/survey.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { deriveProjectIdentity } from "./project-name.js";
|
|
4
|
+
const APP_DIRECTORIES = ["app", join("src", "app")];
|
|
5
|
+
const LAYOUT_NAMES = ["layout.tsx", "layout.jsx", "layout.ts", "layout.js"];
|
|
6
|
+
const NEXT_CONFIG_NAMES = [
|
|
7
|
+
"next.config.ts",
|
|
8
|
+
"next.config.mjs",
|
|
9
|
+
"next.config.js",
|
|
10
|
+
"next.config.mts",
|
|
11
|
+
];
|
|
12
|
+
const STYLESHEET_NAMES = ["globals.css", "global.css", "index.css"];
|
|
13
|
+
async function exists(path) {
|
|
14
|
+
return stat(path).then(() => true, () => false);
|
|
15
|
+
}
|
|
16
|
+
async function firstExisting(root, candidates) {
|
|
17
|
+
for (const candidate of candidates) {
|
|
18
|
+
if (await exists(join(root, candidate)))
|
|
19
|
+
return candidate;
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
function readDependencyNames(manifest) {
|
|
24
|
+
const names = new Set();
|
|
25
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
26
|
+
const section = manifest[field];
|
|
27
|
+
if (typeof section === "object" && section !== null) {
|
|
28
|
+
Object.keys(section).forEach((name) => names.add(name));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return names;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A project name has to survive being turned into an Adjacent slug. An existing
|
|
35
|
+
* application may be called anything at all, including a scoped name, so the
|
|
36
|
+
* unusable parts are dropped rather than refusing to work on the project.
|
|
37
|
+
*/
|
|
38
|
+
export function toProjectName(packageName, fallback) {
|
|
39
|
+
const raw = typeof packageName === "string" ? packageName : "";
|
|
40
|
+
const withoutScope = raw.includes("/") ? (raw.split("/").pop() ?? "") : raw;
|
|
41
|
+
const cleaned = withoutScope
|
|
42
|
+
.toLowerCase()
|
|
43
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
44
|
+
.replace(/^-+|-+$/g, "")
|
|
45
|
+
.replace(/-{2,}/g, "-");
|
|
46
|
+
return cleaned.length > 0 ? cleaned : fallback;
|
|
47
|
+
}
|
|
48
|
+
export class NotANextApplicationError extends Error {
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Reads the shape of an existing Next.js application. Everything Adjacent later
|
|
52
|
+
* writes or rewrites is decided from this, so detection stays in one place.
|
|
53
|
+
*/
|
|
54
|
+
export async function surveyProject(root) {
|
|
55
|
+
const manifestPath = join(root, "package.json");
|
|
56
|
+
const rawManifest = await readFile(manifestPath, "utf8").catch(() => undefined);
|
|
57
|
+
if (rawManifest === undefined) {
|
|
58
|
+
throw new NotANextApplicationError(`No package.json found in ${root}. Run this inside a Next.js application, or create one with: adj create my-app`);
|
|
59
|
+
}
|
|
60
|
+
let packageJson;
|
|
61
|
+
try {
|
|
62
|
+
packageJson = JSON.parse(rawManifest);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
66
|
+
throw new NotANextApplicationError(`Could not read ${manifestPath}: ${detail}`);
|
|
67
|
+
}
|
|
68
|
+
const dependencies = readDependencyNames(packageJson);
|
|
69
|
+
if (!dependencies.has("next")) {
|
|
70
|
+
throw new NotANextApplicationError(`${root} does not depend on Next.js. Adjacent adds to a Next.js application; create one with: adj create my-app`);
|
|
71
|
+
}
|
|
72
|
+
const appDirectory = await firstExisting(root, APP_DIRECTORIES);
|
|
73
|
+
if (appDirectory === undefined) {
|
|
74
|
+
throw new NotANextApplicationError(`No App Router directory found in ${root}. Adjacent needs an app/ or src/app/ directory; the Pages Router is not supported.`);
|
|
75
|
+
}
|
|
76
|
+
const layoutFile = await firstExisting(root, LAYOUT_NAMES.map((name) => join(appDirectory, name)));
|
|
77
|
+
if (layoutFile === undefined) {
|
|
78
|
+
throw new NotANextApplicationError(`No root layout found in ${join(root, appDirectory)}. Adjacent wraps the root layout, so one has to exist.`);
|
|
79
|
+
}
|
|
80
|
+
const globalsCss = await firstExisting(root, STYLESHEET_NAMES.map((name) => join(appDirectory, name)));
|
|
81
|
+
return {
|
|
82
|
+
appDirectory,
|
|
83
|
+
...(globalsCss === undefined ? null : { globalsCss }),
|
|
84
|
+
hasAdjacentConfig: await exists(join(root, "app.config.ts")),
|
|
85
|
+
hasTailwind: dependencies.has("tailwindcss") || dependencies.has("@tailwindcss/postcss"),
|
|
86
|
+
identity: deriveProjectIdentity(toProjectName(packageJson.name, "adjacent-app")),
|
|
87
|
+
layoutFile,
|
|
88
|
+
...(await firstExisting(root, NEXT_CONFIG_NAMES).then((file) => file === undefined ? null : { nextConfigFile: file })),
|
|
89
|
+
packageJson,
|
|
90
|
+
root,
|
|
91
|
+
typescript: layoutFile.endsWith(".tsx") || layoutFile.endsWith(".ts"),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=survey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"survey.js","sourceRoot":"","sources":["../src/survey.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Values substituted into template files. */
|
|
2
|
+
export type TemplateValues = Readonly<Record<string, string>>;
|
|
3
|
+
export declare function isTextTemplate(path: string): boolean;
|
|
4
|
+
export declare function applyTemplateName(name: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Substitutes `{{placeholder}}` values. An unknown placeholder is an authoring
|
|
7
|
+
* mistake in the bundled template, so it fails loudly rather than shipping a
|
|
8
|
+
* project with `{{appName}}` rendered into its manifest.
|
|
9
|
+
*/
|
|
10
|
+
export declare function renderTemplate(source: string, values: TemplateValues): string;
|
|
11
|
+
export interface TemplateEntry {
|
|
12
|
+
/** Path relative to the template root, using forward slashes. */
|
|
13
|
+
readonly path: string;
|
|
14
|
+
}
|
|
15
|
+
/** Lists a template directory depth first. */
|
|
16
|
+
export declare function collectTemplateEntries(templateRoot: string, base?: string): Promise<readonly TemplateEntry[]>;
|
|
17
|
+
export declare function toDestinationPath(templateRelativePath: string): string;
|
|
18
|
+
/** Receives each rendered file. Returning false means one was already there. */
|
|
19
|
+
export type TemplateSink = (destinationPath: string, contents: Buffer | string) => Promise<boolean>;
|
|
20
|
+
export interface RenderTemplateTreeOptions {
|
|
21
|
+
/** Rewrites a destination path, for a project that keeps sources under `src/`. */
|
|
22
|
+
readonly relocate?: (destinationPath: string) => string;
|
|
23
|
+
readonly sink: TemplateSink;
|
|
24
|
+
readonly templateRoot: string;
|
|
25
|
+
readonly values: TemplateValues;
|
|
26
|
+
}
|
|
27
|
+
/** Renders a bundled template tree through the sink: text rendered, binary copied. */
|
|
28
|
+
export declare function renderTemplateTree({ relocate, sink, templateRoot, values, }: RenderTemplateTreeOptions): Promise<readonly string[]>;
|
|
29
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAGA,8CAA8C;AAC9C,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAqB9D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGpD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAQ7E;AAED,MAAM,WAAW,aAAa;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,8CAA8C;AAC9C,wBAAsB,sBAAsB,CAC1C,YAAY,EAAE,MAAM,EACpB,IAAI,SAAK,GACR,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC,CAgBnC;AAED,wBAAgB,iBAAiB,CAAC,oBAAoB,EAAE,MAAM,GAAG,MAAM,CAKtE;AAED,gFAAgF;AAChF,MAAM,MAAM,YAAY,GAAG,CACzB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,GAAG,MAAM,KACtB,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,MAAM,WAAW,yBAAyB;IACxC,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,MAAM,CAAC;IACxD,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;CACjC;AAED,sFAAsF;AACtF,wBAAsB,kBAAkB,CAAC,EACvC,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,MAAM,GACP,EAAE,yBAAyB,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAiBxD"}
|
package/dist/template.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Only these are rendered. Everything else is copied byte for byte, so an icon
|
|
5
|
+
* is never corrupted by being decoded as text.
|
|
6
|
+
*/
|
|
7
|
+
const TEXT_EXTENSIONS = new Set([".css", ".json", ".md", ".svg", ".ts", ".tsx"]);
|
|
8
|
+
/**
|
|
9
|
+
* A leading dot would make the template's own ignore file apply to this
|
|
10
|
+
* repository, so it is stored without one and restored on the way out.
|
|
11
|
+
*/
|
|
12
|
+
const FILENAME_OVERRIDES = {
|
|
13
|
+
_gitignore: ".gitignore",
|
|
14
|
+
};
|
|
15
|
+
const PLACEHOLDER = /\{\{\s*([a-zA-Z][a-zA-Z0-9]*)\s*\}\}/g;
|
|
16
|
+
/** Platform noise that never belongs in a generated project. */
|
|
17
|
+
const IGNORED_ENTRIES = new Set([".DS_Store"]);
|
|
18
|
+
export function isTextTemplate(path) {
|
|
19
|
+
const dot = path.lastIndexOf(".");
|
|
20
|
+
return dot === -1 ? false : TEXT_EXTENSIONS.has(path.slice(dot));
|
|
21
|
+
}
|
|
22
|
+
export function applyTemplateName(name) {
|
|
23
|
+
return FILENAME_OVERRIDES[name] ?? name;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Substitutes `{{placeholder}}` values. An unknown placeholder is an authoring
|
|
27
|
+
* mistake in the bundled template, so it fails loudly rather than shipping a
|
|
28
|
+
* project with `{{appName}}` rendered into its manifest.
|
|
29
|
+
*/
|
|
30
|
+
export function renderTemplate(source, values) {
|
|
31
|
+
return source.replace(PLACEHOLDER, (_match, key) => {
|
|
32
|
+
const value = values[key];
|
|
33
|
+
if (value === undefined) {
|
|
34
|
+
throw new Error(`The bundled template referenced an unknown value: {{${key}}}`);
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/** Lists a template directory depth first. */
|
|
40
|
+
export async function collectTemplateEntries(templateRoot, base = "") {
|
|
41
|
+
const entries = await readdir(join(templateRoot, base), { withFileTypes: true });
|
|
42
|
+
const collected = [];
|
|
43
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
44
|
+
if (IGNORED_ENTRIES.has(entry.name))
|
|
45
|
+
continue;
|
|
46
|
+
const sourcePath = base === "" ? entry.name : `${base}/${entry.name}`;
|
|
47
|
+
if (entry.isDirectory()) {
|
|
48
|
+
collected.push(...(await collectTemplateEntries(templateRoot, sourcePath)));
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
collected.push({ path: sourcePath });
|
|
52
|
+
}
|
|
53
|
+
return collected;
|
|
54
|
+
}
|
|
55
|
+
export function toDestinationPath(templateRelativePath) {
|
|
56
|
+
return templateRelativePath
|
|
57
|
+
.split("/")
|
|
58
|
+
.map((segment) => applyTemplateName(segment))
|
|
59
|
+
.join("/");
|
|
60
|
+
}
|
|
61
|
+
/** Renders a bundled template tree through the sink: text rendered, binary copied. */
|
|
62
|
+
export async function renderTemplateTree({ relocate, sink, templateRoot, values, }) {
|
|
63
|
+
const entries = await collectTemplateEntries(templateRoot);
|
|
64
|
+
const written = [];
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
const source = join(templateRoot, ...entry.path.split("/"));
|
|
67
|
+
const mapped = toDestinationPath(entry.path);
|
|
68
|
+
const destinationPath = relocate ? relocate(mapped) : mapped;
|
|
69
|
+
const contents = isTextTemplate(entry.path)
|
|
70
|
+
? renderTemplate(await readFile(source, "utf8"), values)
|
|
71
|
+
: await readFile(source);
|
|
72
|
+
if (await sink(destinationPath, contents))
|
|
73
|
+
written.push(destinationPath);
|
|
74
|
+
}
|
|
75
|
+
return written;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAKjC;;;GAGG;AACH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;AAEjF;;;GAGG;AACH,MAAM,kBAAkB,GAAqC;IAC3D,UAAU,EAAE,YAAY;CACzB,CAAC;AAEF,MAAM,WAAW,GAAG,uCAAuC,CAAC;AAE5D,gEAAgE;AAChE,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;AAE/C,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,kBAAkB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AAC1C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,MAAsB;IACnE,OAAO,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,GAAW,EAAE,EAAE;QACzD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,uDAAuD,GAAG,IAAI,CAAC,CAAC;QAClF,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC;AAOD,8CAA8C;AAC9C,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,YAAoB,EACpB,IAAI,GAAG,EAAE;IAET,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACjF,MAAM,SAAS,GAAoB,EAAE,CAAC;IAEtC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACzE,IAAI,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,SAAS;QAC9C,MAAM,UAAU,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAEtE,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,sBAAsB,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;YAC5E,SAAS;QACX,CAAC;QACD,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,oBAA4B;IAC5D,OAAO,oBAAoB;SACxB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;SAC5C,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAgBD,sFAAsF;AACtF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,EACvC,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,MAAM,GACoB;IAC1B,MAAM,OAAO,GAAG,MAAM,sBAAsB,CAAC,YAAY,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7C,MAAM,eAAe,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAE7D,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC;YACzC,CAAC,CAAC,cAAc,CAAC,MAAM,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YACxD,CAAC,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;QAE3B,IAAI,MAAM,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/ui.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare function intro(message: string): void;
|
|
2
|
+
export declare function step(message: string): void;
|
|
3
|
+
export declare function success(message: string): void;
|
|
4
|
+
export declare function outro(message: string): void;
|
|
5
|
+
export declare function selectItem<T>(options: {
|
|
6
|
+
readonly message: string;
|
|
7
|
+
readonly items: readonly {
|
|
8
|
+
readonly hint?: string;
|
|
9
|
+
readonly label: string;
|
|
10
|
+
readonly value: T;
|
|
11
|
+
}[];
|
|
12
|
+
}): Promise<T>;
|
|
13
|
+
export declare function task<T>(message: string, operation: () => Promise<T>): Promise<T>;
|
|
14
|
+
//# sourceMappingURL=ui.d.ts.map
|
package/dist/ui.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAEA,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAG3C;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAG3C;AAED,wBAAsB,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS;QACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;KACnB,EAAE,CAAC;CACL,GAAG,OAAO,CAAC,CAAC,CAAC,CAYb;AAED,wBAAsB,IAAI,CAAC,CAAC,EAC1B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC,CAgBZ"}
|
package/dist/ui.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as prompts from "@clack/prompts";
|
|
2
|
+
export function intro(message) {
|
|
3
|
+
if (process.stdout.isTTY)
|
|
4
|
+
prompts.intro(message);
|
|
5
|
+
else
|
|
6
|
+
console.log(message);
|
|
7
|
+
}
|
|
8
|
+
export function step(message) {
|
|
9
|
+
prompts.log.step(message);
|
|
10
|
+
}
|
|
11
|
+
export function success(message) {
|
|
12
|
+
prompts.log.success(message);
|
|
13
|
+
}
|
|
14
|
+
export function outro(message) {
|
|
15
|
+
if (process.stdout.isTTY)
|
|
16
|
+
prompts.outro(message);
|
|
17
|
+
else
|
|
18
|
+
console.log(message);
|
|
19
|
+
}
|
|
20
|
+
export async function selectItem(options) {
|
|
21
|
+
const selected = await prompts.select({
|
|
22
|
+
message: options.message,
|
|
23
|
+
options: options.items.map((item, index) => item.hint
|
|
24
|
+
? { hint: item.hint, label: item.label, value: index }
|
|
25
|
+
: { label: item.label, value: index }),
|
|
26
|
+
});
|
|
27
|
+
if (prompts.isCancel(selected)) {
|
|
28
|
+
prompts.cancel("Operation cancelled.");
|
|
29
|
+
throw new Error("Operation cancelled.");
|
|
30
|
+
}
|
|
31
|
+
return options.items[selected].value;
|
|
32
|
+
}
|
|
33
|
+
export async function task(message, operation) {
|
|
34
|
+
if (!process.stdout.isTTY) {
|
|
35
|
+
step(message);
|
|
36
|
+
return operation();
|
|
37
|
+
}
|
|
38
|
+
const progress = prompts.spinner();
|
|
39
|
+
progress.start(message);
|
|
40
|
+
try {
|
|
41
|
+
const result = await operation();
|
|
42
|
+
progress.stop(message);
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
progress.stop(`Failed: ${message}`);
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=ui.js.map
|
package/dist/ui.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAE1C,MAAM,UAAU,KAAK,CAAC,OAAe;IACnC,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK;QAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;;QAC5C,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,OAAe;IAClC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,OAAe;IACrC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,OAAe;IACnC,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK;QAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;;QAC5C,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAI,OAOnC;IACC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,MAAM,CAAS;QAC5C,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI;YACnD,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE;YACtD,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;KACzC,CAAC,CAAC;IACH,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAE,CAAC,KAAK,CAAC;AACxC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CACxB,OAAe,EACf,SAA2B;IAE3B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,OAAO,CAAC,CAAC;QACd,OAAO,SAAS,EAAE,CAAC;IACrB,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IACnC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACxB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACvB,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,QAAQ,CAAC,IAAI,CAAC,WAAW,OAAO,EAAE,CAAC,CAAC;QACpC,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "adjacent-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The adj command: create and convert Adjacent apps, and run them on Android over a trusted tunnel.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"pwa",
|
|
8
|
+
"android",
|
|
9
|
+
"twa",
|
|
10
|
+
"adjacent"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "Eric Kweyunga",
|
|
14
|
+
"homepage": "https://github.com/erickweyunga/adjacent-framework/blob/main/docs/cli.md",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/erickweyunga/adjacent-framework.git",
|
|
18
|
+
"directory": "packages/adjacent-cli"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/erickweyunga/adjacent-framework/issues"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"bin": {
|
|
25
|
+
"adj": "./dist/bin.js"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"templates",
|
|
30
|
+
"README.md",
|
|
31
|
+
"CHANGELOG.md",
|
|
32
|
+
"LICENSE"
|
|
33
|
+
],
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=20.12.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc -p tsconfig.build.json",
|
|
42
|
+
"test": "bun test",
|
|
43
|
+
"typecheck": "tsc --noEmit",
|
|
44
|
+
"prepublishOnly": "npm run build"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@clack/prompts": "^1.7.0",
|
|
48
|
+
"cac": "^7.0.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/bun": "^1.3.13",
|
|
52
|
+
"@types/node": "^20",
|
|
53
|
+
"typescript": "^5"
|
|
54
|
+
},
|
|
55
|
+
"exports": {
|
|
56
|
+
".": {
|
|
57
|
+
"types": "./dist/index.d.ts",
|
|
58
|
+
"import": "./dist/index.js",
|
|
59
|
+
"default": "./dist/index.js"
|
|
60
|
+
},
|
|
61
|
+
"./package.json": "./package.json"
|
|
62
|
+
},
|
|
63
|
+
"main": "./dist/index.js",
|
|
64
|
+
"types": "./dist/index.d.ts"
|
|
65
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { defineConfig } from "adjacent-core";
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
app: {
|
|
5
|
+
description: "A mobile-first progressive web application built with Adjacent.",
|
|
6
|
+
name: "{{appName}}",
|
|
7
|
+
shortName: "{{shortName}}",
|
|
8
|
+
slug: "{{slug}}",
|
|
9
|
+
},
|
|
10
|
+
appearance: {
|
|
11
|
+
backgroundColor: "#ffffff",
|
|
12
|
+
themeColor: "#171717",
|
|
13
|
+
},
|
|
14
|
+
// The Android wrapper reads these through the generated web manifest.
|
|
15
|
+
android: {
|
|
16
|
+
navigationColor: "#ffffff",
|
|
17
|
+
navigationColorDark: "#0c0c0e",
|
|
18
|
+
themeColorDark: "#0c0c0e",
|
|
19
|
+
},
|
|
20
|
+
offline: true,
|
|
21
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Tab, Tabs } from "adjacent-ui/navigation";
|
|
4
|
+
import Link from "next/link";
|
|
5
|
+
import { usePathname } from "next/navigation";
|
|
6
|
+
|
|
7
|
+
function HomeIcon() {
|
|
8
|
+
return (
|
|
9
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
|
|
10
|
+
<path d="M12 3 3 10.5V21h6v-6h6v6h6V10.5L12 3Z" />
|
|
11
|
+
</svg>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function SettingsIcon() {
|
|
16
|
+
return (
|
|
17
|
+
<svg aria-hidden="true" viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
|
|
18
|
+
<path d="M12 15.5A3.5 3.5 0 1 1 12 8.5a3.5 3.5 0 0 1 0 7Zm7.4-2.5a7.6 7.6 0 0 0 0-2l2-1.6-2-3.4-2.4 1a7.6 7.6 0 0 0-1.7-1L14.9 3H9.1l-.4 2.6a7.6 7.6 0 0 0-1.7 1l-2.4-1-2 3.4L4.6 11a7.6 7.6 0 0 0 0 2l-2 1.6 2 3.4 2.4-1a7.6 7.6 0 0 0 1.7 1l.4 2.6h5.8l.4-2.6a7.6 7.6 0 0 0 1.7-1l2.4 1 2-3.4-2-1.6Z" />
|
|
19
|
+
</svg>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* One navigation, two presentations: a bottom bar on a phone and a left rail
|
|
25
|
+
* from 768px. Which one shows is decided in CSS, so there is no hydration flash.
|
|
26
|
+
*/
|
|
27
|
+
export function Navigation() {
|
|
28
|
+
const pathname = usePathname();
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Tabs>
|
|
32
|
+
<Tab
|
|
33
|
+
active={pathname === "/"}
|
|
34
|
+
icon={<HomeIcon />}
|
|
35
|
+
label="Home"
|
|
36
|
+
render={<Link href="/" />}
|
|
37
|
+
/>
|
|
38
|
+
<Tab
|
|
39
|
+
active={pathname.startsWith("/settings")}
|
|
40
|
+
icon={<SettingsIcon />}
|
|
41
|
+
label="Settings"
|
|
42
|
+
render={<Link href="/settings" />}
|
|
43
|
+
/>
|
|
44
|
+
</Tabs>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" role="img" aria-label="App icon">
|
|
2
|
+
<rect width="100" height="100" fill="#171717" />
|
|
3
|
+
<path fill="#ffffff" fill-rule="evenodd" d="M 50.0 3.0 L 98.5 97.0 L 1.5 97.0 Z M 50.0 25.29 L 71.01 66.0 L 28.99 66.0 Z M 23.06 77.5 L 76.94 77.5 L 87.0 97.0 L 13.0 97.0 Z" />
|
|
4
|
+
</svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { NavigationShell } from "adjacent-ui/navigation";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
import { Navigation } from "@/components/navigation";
|
|
5
|
+
|
|
6
|
+
export default function AppLayout({ children }: { readonly children: ReactNode }) {
|
|
7
|
+
return (
|
|
8
|
+
<NavigationShell className="bg-background" navigation={<Navigation />}>
|
|
9
|
+
{children}
|
|
10
|
+
</NavigationShell>
|
|
11
|
+
);
|
|
12
|
+
}
|