create-aerobuilt 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 (4) hide show
  1. package/README.md +59 -0
  2. package/index.js +152 -0
  3. package/lib.js +116 -0
  4. package/package.json +28 -0
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # create-aerobuilt
2
+
3
+ Scaffold a new [Aero](https://github.com/aerobuilt/aero) project. Aero is an HTML-first static site generator powered by Vite.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ pnpm create aerobuilt my-app
9
+ cd my-app
10
+ pnpm dev
11
+ ```
12
+
13
+ Also works with npm, yarn, and `pnpm dlx`:
14
+
15
+ ```bash
16
+ npx create-aerobuilt@latest my-app
17
+ yarn create aerobuilt my-app
18
+ pnpm dlx create-aerobuilt my-app
19
+ ```
20
+
21
+ ## Options
22
+
23
+ | Argument | Description | Default |
24
+ | -------- | -------------------------- | ------------ |
25
+ | `<dir>` | Project name and directory | _(required)_ |
26
+
27
+ ## What it does
28
+
29
+ 1. Copies the minimal template into a new directory
30
+ 2. Rewrites `package.json` with your project name
31
+ 3. Auto-detects your package manager (pnpm > yarn > npm) and installs dependencies
32
+ 4. Prints next steps
33
+
34
+ After scaffolding, the project has `aerobuilt` as its only framework dependency.
35
+
36
+ ## Project structure
37
+
38
+ ```
39
+ my-app/
40
+ ├── client/
41
+ │ ├── assets/ # Styles, scripts, images
42
+ │ ├── components/ # Reusable .html components
43
+ │ ├── layouts/ # Layout wrappers with <slot>
44
+ │ └── pages/ # File-based routing
45
+ ├── content/
46
+ │ └── site.ts # Global site data
47
+ ├── public/ # Static assets (copied as-is)
48
+ ├── vite.config.ts # Aero Vite plugin
49
+ └── tsconfig.json # Path aliases
50
+ ```
51
+
52
+ ## Links
53
+
54
+ - [GitHub](https://github.com/aerobuilt/aero)
55
+ - [aerobuilt on npm](https://www.npmjs.com/package/aerobuilt)
56
+
57
+ ## License
58
+
59
+ MIT
package/index.js ADDED
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { cpSync, mkdirSync, existsSync, statSync, readdirSync, lstatSync } from 'fs'
4
+ import { dirname, join, basename } from 'path'
5
+ import { fileURLToPath } from 'url'
6
+ import { spawnSync } from 'child_process'
7
+ import { parseArgs, rewritePackageJson, writeReadme, findWorkspaceRoot } from './lib.js'
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url))
10
+ const startPkgDir = __dirname
11
+ const pkgRoot = join(startPkgDir, 'node_modules')
12
+ const APPS_DIR = 'dist'
13
+
14
+ const TEMPLATES = ['minimal']
15
+ const DEFAULT_TEMPLATE = 'minimal'
16
+
17
+ function resolveTemplatePath(templateName) {
18
+ const pkgName = `@aerobuilt/template-${templateName}`
19
+ const templatePath = join(pkgRoot, pkgName)
20
+ if (!existsSync(templatePath)) {
21
+ console.error(
22
+ `create-aerobuilt: template "${templateName}" not found (expected ${templatePath}).`,
23
+ )
24
+ console.error('Install dependencies with: pnpm install')
25
+ process.exit(1)
26
+ }
27
+ return templatePath
28
+ }
29
+
30
+ function copyTemplate(src, dest) {
31
+ const ignore = name => {
32
+ if (name === 'node_modules') return true
33
+ if (name === '.git') return true
34
+ if (name === 'dist' || name === '.output') return true
35
+ if (name.endsWith('.log') || name === '.DS_Store') return true
36
+ if (name === '.vite' || name === '.nitro') return true
37
+ return false
38
+ }
39
+ mkdirSync(dest, { recursive: true })
40
+ cpSync(src, dest, {
41
+ recursive: true,
42
+ dereference: true,
43
+ filter: source => {
44
+ const name = basename(source)
45
+ return !ignore(name)
46
+ },
47
+ })
48
+ }
49
+
50
+ function isInMonorepo() {
51
+ try {
52
+ const templatePath = join(pkgRoot, `@aerobuilt/template-${DEFAULT_TEMPLATE}`)
53
+ if (!existsSync(templatePath)) return false
54
+ return lstatSync(templatePath).isSymbolicLink()
55
+ } catch {
56
+ return false
57
+ }
58
+ }
59
+
60
+ function installInMonorepo(targetDir) {
61
+ const root = findWorkspaceRoot(targetDir)
62
+ if (!root) {
63
+ console.error('create-aerobuilt: could not find workspace root (pnpm-workspace.yaml).')
64
+ process.exit(1)
65
+ }
66
+ const r = spawnSync('pnpm', ['install', '--no-frozen-lockfile'], {
67
+ stdio: 'inherit',
68
+ cwd: root,
69
+ shell: true,
70
+ })
71
+ if (r.status !== 0) {
72
+ console.error(
73
+ 'create-aerobuilt: pnpm install failed. Run "pnpm install" from the repo root.',
74
+ )
75
+ process.exit(1)
76
+ }
77
+ }
78
+
79
+ function installStandalone(targetDir) {
80
+ const hasPnpm = existsSync(join(targetDir, 'pnpm-lock.yaml'))
81
+ const hasNpm = existsSync(join(targetDir, 'package-lock.json'))
82
+ const hasYarn = existsSync(join(targetDir, 'yarn.lock'))
83
+ const cmd = hasPnpm ? 'pnpm' : hasYarn ? 'yarn' : 'npm'
84
+ const args = hasPnpm || hasYarn ? ['install'] : ['install']
85
+ const r = spawnSync(cmd, args, { stdio: 'inherit', cwd: targetDir, shell: true })
86
+ if (r.status !== 0) {
87
+ console.error(
88
+ `create-aerobuilt: ${cmd} install failed. Run "${cmd} install" in the project directory.`,
89
+ )
90
+ process.exit(1)
91
+ }
92
+ }
93
+
94
+ function main() {
95
+ const { target, template } = parseArgs(process.argv)
96
+
97
+ if (!target) {
98
+ console.error('create-aerobuilt: missing target directory.')
99
+ console.error('Usage: pnpm run create-aerobuilt <dir>')
100
+ console.error('Example: pnpm run create-aerobuilt my-app')
101
+ process.exit(1)
102
+ }
103
+
104
+ if (!TEMPLATES.includes(template)) {
105
+ console.error(
106
+ `create-aerobuilt: unknown template "${template}". Use one of: ${TEMPLATES.join(', ')}`,
107
+ )
108
+ process.exit(1)
109
+ }
110
+
111
+ // Run from packages/create-aerobuilt: scaffold into packages/create-aerobuilt/dist/<target>
112
+ const inMonorepo = isInMonorepo()
113
+ const targetDir = inMonorepo
114
+ ? join(startPkgDir, APPS_DIR, target)
115
+ : join(process.cwd(), target)
116
+
117
+ if (inMonorepo) {
118
+ mkdirSync(join(startPkgDir, APPS_DIR), { recursive: true })
119
+ }
120
+
121
+ if (existsSync(targetDir)) {
122
+ const stat = statSync(targetDir)
123
+ if (!stat.isDirectory()) {
124
+ console.error(`create-aerobuilt: "${target}" exists and is not a directory.`)
125
+ process.exit(1)
126
+ }
127
+ const files = readdirSync(targetDir)
128
+ if (files.length > 0) {
129
+ console.error(`create-aerobuilt: directory "${target}" already exists and is not empty.`)
130
+ process.exit(1)
131
+ }
132
+ }
133
+
134
+ const templatePath = resolveTemplatePath(template)
135
+ console.log(`Creating Aero app in ${target} from template "${template}"...`)
136
+ copyTemplate(templatePath, targetDir)
137
+ rewritePackageJson(targetDir, target, inMonorepo)
138
+ writeReadme(targetDir, target, template)
139
+ console.log('Installing dependencies...')
140
+ if (inMonorepo) {
141
+ installInMonorepo(targetDir)
142
+ } else {
143
+ installStandalone(targetDir)
144
+ }
145
+ console.log('')
146
+ console.log('Done. Next steps:')
147
+ console.log(` cd ${targetDir}`)
148
+ console.log(' pnpm dev')
149
+ console.log('')
150
+ }
151
+
152
+ main()
package/lib.js ADDED
@@ -0,0 +1,116 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'fs'
2
+ import { dirname, join } from 'path'
3
+
4
+ const DEFAULT_TEMPLATE = 'minimal'
5
+
6
+ /**
7
+ * Parse CLI argv into { target, template }.
8
+ * @param {string[]} argv - process.argv
9
+ * @returns {{ target: string | null, template: string }}
10
+ */
11
+ export function parseArgs(argv) {
12
+ const args = argv.slice(2)
13
+ let target = null
14
+ let template = DEFAULT_TEMPLATE
15
+ for (let i = 0; i < args.length; i++) {
16
+ if (args[i] === '--template' && args[i + 1]) {
17
+ template = args[++i]
18
+ continue
19
+ }
20
+ if (!args[i].startsWith('-') && !target) {
21
+ target = args[i]
22
+ }
23
+ }
24
+ return { target, template }
25
+ }
26
+
27
+ /**
28
+ * Rewrite package.json in targetDir: set name to projectName; if !inMonorepo, replace workspace:* with *.
29
+ * @param {string} targetDir
30
+ * @param {string} projectName
31
+ * @param {boolean} inMonorepo
32
+ */
33
+ export function rewritePackageJson(targetDir, projectName, inMonorepo) {
34
+ const pkgPath = join(targetDir, 'package.json')
35
+ if (!existsSync(pkgPath)) return
36
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
37
+ pkg.name = projectName
38
+ if (!inMonorepo) {
39
+ const rewrite = deps => {
40
+ if (!deps) return
41
+ for (const key of Object.keys(deps)) {
42
+ if (deps[key] === 'workspace:*') deps[key] = '*'
43
+ }
44
+ }
45
+ rewrite(pkg.dependencies)
46
+ rewrite(pkg.devDependencies)
47
+ }
48
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
49
+ }
50
+
51
+ /**
52
+ * Walk up from fromDir to find directory containing pnpm-workspace.yaml.
53
+ * @param {string} fromDir
54
+ * @returns {string | null}
55
+ */
56
+ export function findWorkspaceRoot(fromDir) {
57
+ let dir = fromDir
58
+ for (;;) {
59
+ if (existsSync(join(dir, 'pnpm-workspace.yaml'))) return dir
60
+ const parent = dirname(dir)
61
+ if (parent === dir) return null
62
+ dir = parent
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Write a project-appropriate README.md into targetDir.
68
+ * @param {string} targetDir
69
+ * @param {string} projectName
70
+ * @param {string} template
71
+ */
72
+ export function writeReadme(targetDir, projectName, template) {
73
+ const lines = [
74
+ `# ${projectName}`,
75
+ '',
76
+ `Built with [Aero](https://github.com/aerobuilt/aero) — an HTML-first static site generator powered by Vite.`,
77
+ '',
78
+ '## Commands',
79
+ '',
80
+ '| Command | Description |',
81
+ '|---|---|',
82
+ '| `pnpm dev` | Start the dev server |',
83
+ '| `pnpm build` | Build for production |',
84
+ '| `pnpm preview` | Preview the built site |',
85
+ ]
86
+
87
+ lines.push(
88
+ '',
89
+ '## Project Structure',
90
+ '',
91
+ '```',
92
+ `${projectName}/`,
93
+ '├── client/',
94
+ '│ ├── assets/ # Styles, scripts, images',
95
+ '│ ├── components/ # Reusable .html components',
96
+ '│ ├── layouts/ # Layout wrappers with <slot>',
97
+ '│ └── pages/ # File-based routing',
98
+ '├── content/',
99
+ '│ └── site.ts # Global site data',
100
+ '├── public/ # Static assets (copied as-is)',
101
+ '├── vite.config.ts # Aero Vite plugin',
102
+ '└── tsconfig.json # Path aliases',
103
+ '```',
104
+ )
105
+
106
+ lines.push(
107
+ '',
108
+ '## Learn More',
109
+ '',
110
+ '- [Aero on GitHub](https://github.com/aerobuilt/aero)',
111
+ '- [aerobuilt on npm](https://www.npmjs.com/package/aerobuilt)',
112
+ '',
113
+ )
114
+
115
+ writeFileSync(join(targetDir, 'README.md'), lines.join('\n'))
116
+ }
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "create-aerobuilt",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "author": "Jamie Wilson",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/aerobuilt/aero.git",
10
+ "directory": "packages/create-aerobuilt"
11
+ },
12
+ "homepage": "https://github.com/aerobuilt/aero",
13
+ "files": [
14
+ "index.js",
15
+ "lib.js",
16
+ "README.md"
17
+ ],
18
+ "bin": {
19
+ "create-aerobuilt": "index.js"
20
+ },
21
+ "dependencies": {
22
+ "@aerobuilt/template-minimal": "0.1.0"
23
+ },
24
+ "scripts": {
25
+ "create": "node .",
26
+ "create-aerobuilt": "node index.js"
27
+ }
28
+ }