@portosaur/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.
Files changed (40) hide show
  1. package/README.md +52 -0
  2. package/bin/porto.mjs +71 -0
  3. package/package.json +36 -0
  4. package/src/commands/build.mjs +85 -0
  5. package/src/commands/dev.mjs +61 -0
  6. package/src/commands/init.mjs +523 -0
  7. package/src/commands/initCi.mjs +227 -0
  8. package/src/commands/providers.mjs +170 -0
  9. package/src/commands/schema.mjs +208 -0
  10. package/src/commands/serve.mjs +29 -0
  11. package/src/index.d.ts +49 -0
  12. package/src/index.mjs +8 -0
  13. package/src/templates/README.md +58 -0
  14. package/src/templates/blog/authors.yml +4 -0
  15. package/src/templates/blog/welcome.md +11 -0
  16. package/src/templates/config.yml +150 -0
  17. package/src/templates/gitignore +9 -0
  18. package/src/templates/notes/index.mdx +9 -0
  19. package/src/templates/notes/welcome.mdx +9 -0
  20. package/src/templates/package.json +14 -0
  21. package/src/templates/registry.yml +107 -0
  22. package/src/templates/static/.nojekyll +0 -0
  23. package/src/templates/static/README.md +1 -0
  24. package/src/templates/workflows/codeberg/.forgejo/workflows/deploy.yml +39 -0
  25. package/src/templates/workflows/github/.github/workflows/deploy.yml +55 -0
  26. package/src/templates/workflows/gitlab/.gitlab-ci.yml +13 -0
  27. package/src/templates/workflows/netlify/netlify.toml +6 -0
  28. package/src/templates/workflows/surge/codeberg/.forgejo/workflows/deploy.yml +23 -0
  29. package/src/templates/workflows/surge/github/.github/workflows/deploy.yml +23 -0
  30. package/src/templates/workflows/surge/gitlab/.gitlab-ci.yml +16 -0
  31. package/src/templates/workflows/surge/sourcehut/.build.yml +26 -0
  32. package/src/templates/workflows/woodpecker/.woodpecker/deploy.yml +21 -0
  33. package/src/utils/git.mjs +52 -0
  34. package/src/utils/index.mjs +7 -0
  35. package/src/utils/interaction.mjs +24 -0
  36. package/src/utils/packageManager.mjs +85 -0
  37. package/src/utils/paths.mjs +33 -0
  38. package/src/utils/platforms.mjs +130 -0
  39. package/src/utils/projectName.mjs +20 -0
  40. package/src/utils/runner.mjs +192 -0
package/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # porto
2
+
3
+ The primary command-line interface for Portosaur.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ # Global installation
9
+ bun install -g @portosaur/cli # or npm install -g @portosaur/cli
10
+
11
+ # Or use directly with bunx/npx
12
+ bunx @portosaur/cli@latest # or npx @portosaur/cli@latest
13
+ ```
14
+
15
+ ## Commands
16
+
17
+ - `porto init [-P name]` — Initialize a new project (interactive mode recommended).
18
+ - `porto init-ci [-h id]` — Set up CI/CD for an existing project.
19
+ - `porto dev` — Start the development server.
20
+ - `porto build` — Generate a static production build.
21
+ - `porto serve` — Preview a production build locally.
22
+ - `porto schema` — Output the JSON schema for validation.
23
+
24
+ ## Usage
25
+
26
+ ```bash
27
+ # Interactive mode (recommended)
28
+ porto init
29
+
30
+ # Or with project name
31
+ porto init -P my-portfolio
32
+
33
+ # With all options (non-interactive)
34
+ porto init -P my-site -p github -h github-pages -u myusername -n "My Name"
35
+
36
+ # Start development
37
+ cd my-portfolio
38
+ bun run dev
39
+
40
+ # Build for production
41
+ bun run build
42
+ ```
43
+
44
+ ## Library Usage
45
+
46
+ If you want to use Portosaur programmatically, install `@portosaur/core`:
47
+
48
+ ```bash
49
+ npm install @portosaur/core
50
+ ```
51
+
52
+ See `@portosaur/core` for API documentation.
package/bin/porto.mjs ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { Command, Argument } from "commander";
4
+ import { porto } from "@portosaur/core";
5
+ import { initCommand } from "../src/commands/init.mjs";
6
+ import { initCiCommand } from "../src/commands/initCi.mjs";
7
+ import { devCommand } from "../src/commands/dev.mjs";
8
+ import { buildCommand } from "../src/commands/build.mjs";
9
+ import { serveCommand } from "../src/commands/serve.mjs";
10
+ import { schemaCommand } from "../src/commands/schema.mjs";
11
+ import { providersCommand } from "../src/commands/providers.mjs";
12
+
13
+ const program = new Command();
14
+
15
+ program
16
+ .name("porto")
17
+ .description("CLI for Portosaur — The complete portfolio solution")
18
+ .version(porto.version, "-v, --version", "output the current version")
19
+ .helpOption("--help", "output usage information");
20
+
21
+ program
22
+ .command("init")
23
+ .description("Initialize a new Portosaur project")
24
+ .option("-p, --vcs-provider <id>", "VCS Provider ID")
25
+ .option("-h, --hosting <id>", "Hosting Platform ID")
26
+ .option("-u, --username <user>", "VCS username")
27
+ .option("-n, --name <name>", "Full name for portfolio")
28
+ .option("-P, --project-name <name>", "Project name")
29
+ .option("-k, --no-install", "Skip dependency installation")
30
+ .action((options) => initCommand(options));
31
+
32
+ program
33
+ .command("init-ci")
34
+ .description("Setup CI/CD workflows for an existing project")
35
+ .option("-h, --hosting <id>", "Hosting Platform ID")
36
+ .action((options) => initCiCommand(options));
37
+
38
+ program
39
+ .command("providers")
40
+ .description("List available VCS providers and hosting platforms")
41
+ .addArgument(
42
+ new Argument("[type]", "Filter list by type").choices(["vcs", "hosting"]),
43
+ )
44
+ .action((type) => providersCommand(type));
45
+
46
+ program
47
+ .command("dev [siteDir] [extraArgs...]")
48
+ .alias("start")
49
+ .description("Start the development server")
50
+ .allowUnknownOption()
51
+ .action(devCommand);
52
+
53
+ program
54
+ .command("build [siteDir] [extraArgs...]")
55
+ .description("Build the static site")
56
+ .allowUnknownOption()
57
+ .action(buildCommand);
58
+
59
+ program
60
+ .command("serve [siteDir]")
61
+ .description("Serve the built static site locally")
62
+ .action(serveCommand);
63
+
64
+ program
65
+ .command("schema", { hidden: true })
66
+ .description("Generate the config schema")
67
+ .option("-c, --config <path>", "Path to the source file to scan")
68
+ .option("-o, --output <path>", "Path to output the schema file")
69
+ .action(schemaCommand);
70
+
71
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@portosaur/cli",
3
+ "version": "0.1.0",
4
+ "description": "Portosaur CLI: Personal portfolio site generator.",
5
+ "license": "GPL-3.0-only",
6
+ "author": "soymadip",
7
+ "homepage": "https://soymadip.github.io/portosaur",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/soymadip/portosaur"
11
+ },
12
+ "files": [
13
+ "src",
14
+ "bin",
15
+ "templates"
16
+ ],
17
+ "type": "module",
18
+ "bin": {
19
+ "porto": "./bin/porto.mjs"
20
+ },
21
+ "exports": {
22
+ ".": {
23
+ "import": "./src/index.mjs",
24
+ "default": "./src/index.mjs"
25
+ }
26
+ },
27
+ "types": "./src/index.d.ts",
28
+ "dependencies": {
29
+ "@portosaur/core": "workspace:*",
30
+ "@portosaur/theme": "workspace:*",
31
+ "@portosaur/logger": "workspace:*",
32
+ "@portosaur/wizard": "workspace:*",
33
+ "commander": "^13.1.0",
34
+ "js-yaml": "^4.1.1"
35
+ }
36
+ }
@@ -0,0 +1,85 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import {
4
+ Paths,
5
+ writeConfigShim,
6
+ runDocusaurus,
7
+ validateProject,
8
+ ensureContentDirs,
9
+ } from "../utils/index.mjs";
10
+ import { logger } from "@portosaur/logger";
11
+ import {
12
+ loadUserConfig,
13
+ generateFavicons,
14
+ generateRobotsTxt,
15
+ } from "@portosaur/core";
16
+
17
+ /**
18
+ * Builds the static Portosaur site.
19
+ *
20
+ * This involves:
21
+ * 1. Validating the project structure.
22
+ * 2. Generating dynamic assets (favicons, robots.txt).
23
+ * 3. Compiling the site via Docusaurus.
24
+ */
25
+ export async function buildCommand(siteDir, extraArgs = []) {
26
+ const UserRoot = siteDir
27
+ ? path.resolve(process.cwd(), siteDir)
28
+ : process.cwd();
29
+
30
+ // ------- Setup -------
31
+
32
+ validateProject(UserRoot);
33
+ ensureContentDirs(UserRoot);
34
+
35
+ /*
36
+ * ====================== Path Resolution ======================
37
+ */
38
+
39
+ const portoPaths = {
40
+ root: Paths.root,
41
+ assets: path.join(Paths.theme, "assets"),
42
+ theme: path.join(Paths.theme, "theme"),
43
+ plugins: path.join(Paths.theme, "src/plugins"),
44
+ };
45
+
46
+ try {
47
+ const userConfig = loadUserConfig(UserRoot);
48
+
49
+ // ------- Asset Generation -------
50
+
51
+ logger.info("Generating site assets...");
52
+
53
+ const faviconRes = await generateFavicons(UserRoot, {
54
+ imagePath: userConfig.home_page?.hero?.profile_pic,
55
+ siteTitle: userConfig.site?.title,
56
+ siteTagline: userConfig.site?.tagline,
57
+ staticDirs: ["static"],
58
+ });
59
+
60
+ const configPath = writeConfigShim(UserRoot, portoPaths, {
61
+ extraHeadTags: faviconRes.html,
62
+ });
63
+
64
+ // ------- Docusaurus Build -------
65
+
66
+ logger.info("Building static site...");
67
+
68
+ await runDocusaurus("build", UserRoot, configPath, extraArgs);
69
+
70
+ // ------- Post Build -------
71
+
72
+ await generateRobotsTxt(UserRoot, {
73
+ enable: userConfig.site?.robots_txt?.enable,
74
+ rules: userConfig.site?.robots_txt?.rules,
75
+ customLines: userConfig.site?.robots_txt?.custom_lines,
76
+ siteUrl: userConfig.site?.url,
77
+ baseUrl: userConfig.site?.path,
78
+ });
79
+
80
+ logger.success("Build completed successfully!");
81
+ } catch (error) {
82
+ logger.error(`Build failed: ${error.message}`);
83
+ process.exit(1);
84
+ }
85
+ }
@@ -0,0 +1,61 @@
1
+ import path from "path";
2
+ import fs from "fs";
3
+ import {
4
+ Paths,
5
+ writeConfigShim,
6
+ runDocusaurus,
7
+ validateProject,
8
+ ensureContentDirs,
9
+ } from "../utils/index.mjs";
10
+ import { logger } from "@portosaur/logger";
11
+
12
+ export async function devCommand(siteDir, extraArgs = []) {
13
+ const UserRoot = siteDir
14
+ ? path.resolve(process.cwd(), siteDir)
15
+ : process.cwd();
16
+
17
+ validateProject(UserRoot);
18
+ ensureContentDirs(UserRoot);
19
+
20
+ const configYaml = ["config.yaml", "config.yml"].find((file) =>
21
+ fs.existsSync(path.join(UserRoot, file)),
22
+ );
23
+
24
+ /*
25
+ * ====================== Path Resolution ======================
26
+ */
27
+
28
+ const portoPaths = {
29
+ root: Paths.root,
30
+ assets: path.join(Paths.theme, "assets"),
31
+ theme: path.join(Paths.theme, "theme"),
32
+ plugins: path.join(Paths.theme, "src/plugins"),
33
+ };
34
+
35
+ try {
36
+ const configPath = writeConfigShim(UserRoot, portoPaths);
37
+
38
+ // Watch for config.yml changes to trigger Docusaurus reload
39
+ if (configYaml) {
40
+ const configYamlPath = path.join(UserRoot, configYaml);
41
+ const watcher = fs.watch(configYamlPath, (eventType) => {
42
+ if (eventType === "change") {
43
+ logger.info(`Detected change in ${configYaml}, reloading...`);
44
+ // Touch the shim config to trigger Docusaurus reload
45
+ const now = new Date();
46
+ fs.utimesSync(configPath, now, now);
47
+ }
48
+ });
49
+
50
+ process.on("SIGINT", () => {
51
+ watcher.close();
52
+ process.exit();
53
+ });
54
+ }
55
+
56
+ await runDocusaurus("start", UserRoot, configPath, extraArgs);
57
+ } catch (error) {
58
+ logger.error(`Failed to start dev server: ${error.message}`);
59
+ process.exit(1);
60
+ }
61
+ }