@olonjs/cli 3.0.78

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/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@olonjs/cli",
3
+ "version": "3.0.78",
4
+ "description": "The Sovereign CLI Engine for OlonJS.",
5
+ "type": "module",
6
+ "bin": {
7
+ "olonjs": "./src/index.js",
8
+ "jsonpages": "./src/index.js"
9
+ },
10
+ "files": [
11
+ "src",
12
+ "assets/src_tenant_alpha.sh",
13
+ "assets/templates"
14
+ ],
15
+ "author": "JsonPages Team",
16
+ "license": "MIT",
17
+ "homepage": "https://github.com/olonjs/npm-jpcore#readme",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/olonjs/npm-jpcore.git",
21
+ "directory": "packages/cli"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/olonjs/npm-jpcore/issues"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "scripts": {
30
+ "build": "tsc -p .",
31
+ "check:templates": "node ../../scripts/check-cli-templates.mjs"
32
+ },
33
+ "dependencies": {
34
+ "@olonjs/stack": "^1.0.0",
35
+ "chalk": "^5.3.0",
36
+ "commander": "^12.1.0",
37
+ "execa": "^9.0.2",
38
+ "fs-extra": "^11.2.0",
39
+ "ora": "^8.0.1"
40
+ },
41
+ "devDependencies": {
42
+ "@types/fs-extra": "^11.0.4",
43
+ "@types/node": "^22.13.1",
44
+ "typescript": "^5.7.3"
45
+ }
46
+ }
package/src/index.js ADDED
@@ -0,0 +1,231 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import fs from 'fs-extra';
5
+ import path from 'path';
6
+ import { execa } from 'execa';
7
+ import ora from 'ora';
8
+ import { fileURLToPath } from 'url';
9
+
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+ const CLI_ASSETS_DIR = path.resolve(__dirname, '../assets');
13
+ const TEMPLATES_DIR = path.join(CLI_ASSETS_DIR, 'templates');
14
+ const LEGACY_ALPHA_DNA_PATH = path.join(CLI_ASSETS_DIR, 'src_tenant_alpha.sh');
15
+ const PACKAGE_JSON_PATH = path.resolve(__dirname, '../package.json');
16
+ const CLI_PACKAGE = JSON.parse(await fs.readFile(PACKAGE_JSON_PATH, 'utf-8'));
17
+
18
+ const program = new Command();
19
+
20
+ program
21
+ .name('olonjs')
22
+ .description('OlonJS CLI - Sovereign Projection Engine')
23
+ .version(CLI_PACKAGE.version);
24
+
25
+ const invokedAs = path.basename(process.argv[1] ?? '');
26
+ if (invokedAs === 'jsonpages' || invokedAs === 'jsonpages.cmd') {
27
+ console.warn(chalk.yellow('[compat] `jsonpages` is deprecated. Use `olonjs` instead.'));
28
+ }
29
+
30
+ async function processScriptInNode(scriptPath, targetDir) {
31
+ const content = await fs.readFile(scriptPath, 'utf-8');
32
+ const lines = content.split('\n');
33
+
34
+ let captureMode = false;
35
+ let delimiter = '';
36
+ let currentFile = '';
37
+ let fileBuffer = [];
38
+
39
+ for (const line of lines) {
40
+ const trimmed = line.trim();
41
+
42
+ if (captureMode) {
43
+ if (trimmed === delimiter) {
44
+ const filePath = path.join(targetDir, currentFile);
45
+ await fs.outputFile(filePath, fileBuffer.join('\n'));
46
+ captureMode = false;
47
+ fileBuffer = [];
48
+ } else {
49
+ fileBuffer.push(line);
50
+ }
51
+ continue;
52
+ }
53
+
54
+ if (trimmed.startsWith('mkdir -p')) {
55
+ const match = trimmed.match(/"([^"]+)"/) || trimmed.match(/\s+([^\s]+)/);
56
+ const dirPath = match ? match[1].replace(/"/g, '') : null;
57
+ if (dirPath) {
58
+ await fs.ensureDir(path.join(targetDir, dirPath));
59
+ }
60
+ } else if (trimmed.startsWith('cat <<')) {
61
+ const match = trimmed.match(/<<\s*'([^']+)'\s*>\s*"([^"]+)"/);
62
+ if (match) {
63
+ delimiter = match[1];
64
+ currentFile = match[2];
65
+ captureMode = true;
66
+ }
67
+ }
68
+ }
69
+ }
70
+
71
+ function getAvailableTemplates() {
72
+ if (!fs.existsSync(TEMPLATES_DIR)) return [];
73
+ return fs
74
+ .readdirSync(TEMPLATES_DIR, { withFileTypes: true })
75
+ .filter((entry) => entry.isDirectory())
76
+ .map((entry) => entry.name)
77
+ .filter((name) => fs.existsSync(path.join(TEMPLATES_DIR, name, 'src_tenant.sh')))
78
+ .sort((a, b) => a.localeCompare(b));
79
+ }
80
+
81
+ function resolveTemplateScriptPath(templateName) {
82
+ const templatePath = path.join(TEMPLATES_DIR, templateName, 'src_tenant.sh');
83
+ if (fs.existsSync(templatePath)) return templatePath;
84
+ if (templateName === 'alpha' && fs.existsSync(LEGACY_ALPHA_DNA_PATH)) return LEGACY_ALPHA_DNA_PATH;
85
+ return templatePath;
86
+ }
87
+
88
+ program
89
+ .command('new')
90
+ .argument('<type>', 'Type of artifact (tenant)')
91
+ .argument('<name>', 'Name of the new tenant')
92
+ .option('--template <name>', 'Template profile (default: alpha)', 'alpha')
93
+ .option('--agritourism', 'Alias for --template agritourism')
94
+ .option('--script <path>', 'Override default deterministic script path')
95
+ .action(async (type, name, options) => {
96
+ if (type !== 'tenant') {
97
+ console.log(chalk.red('Error: Only "tenant" type is supported.'));
98
+ return;
99
+ }
100
+
101
+ const targetDir = path.join(process.cwd(), name);
102
+ const availableTemplates = getAvailableTemplates();
103
+ const template = options.agritourism ? 'agritourism' : options.template;
104
+ const scriptPath = options.script
105
+ ? path.resolve(process.cwd(), options.script)
106
+ : resolveTemplateScriptPath(template);
107
+
108
+ if (!options.script && !availableTemplates.includes(template) && !(template === 'alpha' && fs.existsSync(LEGACY_ALPHA_DNA_PATH))) {
109
+ console.log(chalk.red(`Error: Unknown template "${template}".`));
110
+ console.log(chalk.yellow(`Available templates: ${availableTemplates.length ? availableTemplates.join(', ') : '(none found)'}`));
111
+ return;
112
+ }
113
+
114
+ if (!fs.existsSync(scriptPath)) {
115
+ console.log(chalk.red(`Error: DNA script not found at ${scriptPath}`));
116
+ console.log(chalk.yellow(`Debug info: template=${template}, assets=${CLI_ASSETS_DIR}`));
117
+ return;
118
+ }
119
+
120
+ console.log(chalk.blue.bold(`\nProjecting Sovereign Tenant: ${name} (template: ${template})\n`));
121
+ const spinner = ora();
122
+
123
+ try {
124
+ spinner.start('Setting up environment (Vite + TS)...');
125
+ await fs.ensureDir(targetDir);
126
+
127
+ const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
128
+ await execa(npmCmd, ['create', 'vite@latest', '.', '--', '--template', 'react-ts'], { cwd: targetDir });
129
+ spinner.succeed('Environment scaffolded.');
130
+
131
+ spinner.start('Wiping default boilerplate...');
132
+ await fs.emptyDir(path.join(targetDir, 'src'));
133
+ const junk = ['App.css', 'App.tsx', 'main.tsx', 'vite-env.d.ts', 'favicon.ico', 'index.html'];
134
+ for (const file of junk) {
135
+ await fs.remove(path.join(targetDir, file)).catch(() => {});
136
+ await fs.remove(path.join(targetDir, 'src', file)).catch(() => {});
137
+ }
138
+ spinner.succeed('Clean slate achieved.');
139
+
140
+ spinner.start('Injecting Sovereign Configurations...');
141
+ await injectInfraFiles(targetDir, name);
142
+ spinner.succeed('Infrastructure configured.');
143
+
144
+ spinner.start('Executing deterministic src projection...');
145
+ await processScriptInNode(scriptPath, targetDir);
146
+ spinner.succeed('Source code and assets projected successfully.');
147
+
148
+ spinner.start('Installing dependencies (this may take a minute)...');
149
+ await execa(npmCmd, ['install'], { cwd: targetDir });
150
+ spinner.succeed(chalk.green.bold('Tenant Ready.'));
151
+
152
+ console.log(`\n${chalk.white.bgBlue(' NEXT STEPS ')}`);
153
+ console.log(` ${chalk.cyan(`cd ${name}`)}`);
154
+ console.log(` ${chalk.cyan('npm run dev')} <- Start development`);
155
+ console.log(` ${chalk.cyan('npm run build')} <- Validate Green Build`);
156
+ console.log(`\nTemplate used: ${template}\n`);
157
+ } catch (error) {
158
+ spinner.fail(chalk.red('Projection failed.'));
159
+ console.error(error);
160
+ }
161
+ });
162
+
163
+ async function injectInfraFiles(targetDir, name) {
164
+ const pkg = {
165
+ name,
166
+ private: true,
167
+ version: '1.0.0',
168
+ type: 'module',
169
+ scripts: {
170
+ dev: 'vite',
171
+ build: 'tsc && vite build',
172
+ preview: 'vite preview',
173
+ },
174
+ };
175
+ await fs.writeJson(path.join(targetDir, 'package.json'), pkg, { spaces: 2 });
176
+
177
+ await fs.ensureDir(path.join(targetDir, 'public', 'assets', 'images'));
178
+ await fs.ensureDir(path.join(targetDir, 'src', 'data', 'config'));
179
+ await fs.ensureDir(path.join(targetDir, 'src', 'data', 'pages'));
180
+
181
+ const tsConfig = `
182
+ {
183
+ "compilerOptions": {
184
+ "target": "ES2020",
185
+ "useDefineForClassFields": true,
186
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
187
+ "module": "ESNext",
188
+ "skipLibCheck": true,
189
+ "moduleResolution": "bundler",
190
+ "allowImportingTsExtensions": true,
191
+ "resolveJsonModule": true,
192
+ "isolatedModules": true,
193
+ "noEmit": true,
194
+ "jsx": "react-jsx",
195
+ "strict": true,
196
+ "noUnusedLocals": true,
197
+ "noUnusedParameters": true,
198
+ "noFallthroughCasesInSwitch": true,
199
+ "baseUrl": ".",
200
+ "paths": {
201
+ "@/*": ["./src/*"]
202
+ }
203
+ },
204
+ "include": ["src"]
205
+ }`;
206
+ await fs.writeFile(path.join(targetDir, 'tsconfig.json'), tsConfig.trim());
207
+
208
+ const shadcnConfig = {
209
+ $schema: 'https://ui.shadcn.com/schema.json',
210
+ style: 'radix-nova',
211
+ rsc: false,
212
+ tsx: true,
213
+ tailwind: {
214
+ config: '',
215
+ css: 'src/index.css',
216
+ baseColor: 'zinc',
217
+ cssVariables: true,
218
+ prefix: '',
219
+ },
220
+ aliases: {
221
+ components: '@/components',
222
+ utils: '@/lib/utils',
223
+ ui: '@/components/ui',
224
+ lib: '@/lib',
225
+ hooks: '@/hooks',
226
+ },
227
+ };
228
+ await fs.writeJson(path.join(targetDir, 'components.json'), shadcnConfig, { spaces: 2 });
229
+ }
230
+
231
+ program.parse();