@jsonpages/cli 3.0.71 → 3.0.73
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/assets/src_tenant_alpha.sh +1438 -39
- package/assets/templates/agritourism/manifest.json +5 -0
- package/assets/templates/agritourism/src_tenant.sh +7702 -0
- package/assets/templates/alpha/manifest.json +5 -0
- package/assets/templates/alpha/src_tenant.sh +9096 -0
- package/package.json +45 -43
- package/src/index.js +72 -70
package/package.json
CHANGED
|
@@ -1,43 +1,45 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@jsonpages/cli",
|
|
3
|
+
"version": "3.0.73",
|
|
4
|
+
"description": "The Sovereign CLI Engine for JsonPages.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"jsonpages": "./src/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"src",
|
|
11
|
+
"assets/src_tenant_alpha.sh",
|
|
12
|
+
"assets/templates"
|
|
13
|
+
],
|
|
14
|
+
"author": "JsonPages Team",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"homepage": "https://github.com/jsonpages/npm-jpcore#readme",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/jsonpages/npm-jpcore.git",
|
|
20
|
+
"directory": "packages/cli"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/jsonpages/npm-jpcore/issues"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc -p .",
|
|
30
|
+
"check:templates": "node ../../scripts/check-cli-templates.mjs"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@jsonpages/stack": "^1.0.0",
|
|
34
|
+
"chalk": "^5.3.0",
|
|
35
|
+
"commander": "^12.1.0",
|
|
36
|
+
"execa": "^9.0.2",
|
|
37
|
+
"fs-extra": "^11.2.0",
|
|
38
|
+
"ora": "^8.0.1"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/fs-extra": "^11.0.4",
|
|
42
|
+
"@types/node": "^22.13.1",
|
|
43
|
+
"typescript": "^5.7.3"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/index.js
CHANGED
|
@@ -7,26 +7,23 @@ import { execa } from 'execa';
|
|
|
7
7
|
import ora from 'ora';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
9
|
|
|
10
|
-
// 🛡️ Risoluzione path ESM
|
|
11
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
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');
|
|
13
15
|
|
|
14
16
|
const program = new Command();
|
|
15
17
|
|
|
16
18
|
program
|
|
17
19
|
.name('jsonpages')
|
|
18
20
|
.description('JsonPages CLI - Sovereign Projection Engine')
|
|
19
|
-
.version('2.0.2');
|
|
21
|
+
.version('2.0.2');
|
|
20
22
|
|
|
21
|
-
/**
|
|
22
|
-
* 🧠 THE UNIVERSAL INTERPRETER
|
|
23
|
-
* Legge lo script bash "DNA" e lo esegue usando le API di Node.js.
|
|
24
|
-
* Rende la CLI compatibile con Windows (PowerShell/CMD) senza bisogno di Bash.
|
|
25
|
-
*/
|
|
26
23
|
async function processScriptInNode(scriptPath, targetDir) {
|
|
27
24
|
const content = await fs.readFile(scriptPath, 'utf-8');
|
|
28
25
|
const lines = content.split('\n');
|
|
29
|
-
|
|
26
|
+
|
|
30
27
|
let captureMode = false;
|
|
31
28
|
let delimiter = '';
|
|
32
29
|
let currentFile = '';
|
|
@@ -35,35 +32,25 @@ async function processScriptInNode(scriptPath, targetDir) {
|
|
|
35
32
|
for (const line of lines) {
|
|
36
33
|
const trimmed = line.trim();
|
|
37
34
|
|
|
38
|
-
// 1. Modalità Cattura (Siamo dentro un cat << 'DELIMITER')
|
|
39
35
|
if (captureMode) {
|
|
40
36
|
if (trimmed === delimiter) {
|
|
41
|
-
// Fine del blocco: Scriviamo su disco
|
|
42
37
|
const filePath = path.join(targetDir, currentFile);
|
|
43
38
|
await fs.outputFile(filePath, fileBuffer.join('\n'));
|
|
44
39
|
captureMode = false;
|
|
45
40
|
fileBuffer = [];
|
|
46
41
|
} else {
|
|
47
|
-
fileBuffer.push(line);
|
|
42
|
+
fileBuffer.push(line);
|
|
48
43
|
}
|
|
49
44
|
continue;
|
|
50
45
|
}
|
|
51
46
|
|
|
52
|
-
// 2. Parsing Comandi Bash -> Node Operations
|
|
53
|
-
|
|
54
|
-
// Rileva: mkdir -p "path"
|
|
55
47
|
if (trimmed.startsWith('mkdir -p')) {
|
|
56
|
-
const match = trimmed.match(/"([^"]+)"/) || trimmed.match(/\s+([^\s]+)/);
|
|
57
|
-
// Supporta sia mkdir -p "foo/bar" che mkdir -p foo/bar
|
|
48
|
+
const match = trimmed.match(/"([^"]+)"/) || trimmed.match(/\s+([^\s]+)/);
|
|
58
49
|
const dirPath = match ? match[1].replace(/"/g, '') : null;
|
|
59
50
|
if (dirPath) {
|
|
60
51
|
await fs.ensureDir(path.join(targetDir, dirPath));
|
|
61
52
|
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Rileva: cat << 'DELIMITER' > "path"
|
|
65
|
-
else if (trimmed.startsWith('cat <<')) {
|
|
66
|
-
// Regex robusta per catturare il delimitatore e il path del file
|
|
53
|
+
} else if (trimmed.startsWith('cat <<')) {
|
|
67
54
|
const match = trimmed.match(/<<\s*'([^']+)'\s*>\s*"([^"]+)"/);
|
|
68
55
|
if (match) {
|
|
69
56
|
delimiter = match[1];
|
|
@@ -71,49 +58,69 @@ async function processScriptInNode(scriptPath, targetDir) {
|
|
|
71
58
|
captureMode = true;
|
|
72
59
|
}
|
|
73
60
|
}
|
|
74
|
-
// Ignora echo, set -e, commenti #, ecc.
|
|
75
61
|
}
|
|
76
62
|
}
|
|
77
63
|
|
|
64
|
+
function getAvailableTemplates() {
|
|
65
|
+
if (!fs.existsSync(TEMPLATES_DIR)) return [];
|
|
66
|
+
return fs
|
|
67
|
+
.readdirSync(TEMPLATES_DIR, { withFileTypes: true })
|
|
68
|
+
.filter((entry) => entry.isDirectory())
|
|
69
|
+
.map((entry) => entry.name)
|
|
70
|
+
.filter((name) => fs.existsSync(path.join(TEMPLATES_DIR, name, 'src_tenant.sh')))
|
|
71
|
+
.sort((a, b) => a.localeCompare(b));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function resolveTemplateScriptPath(templateName) {
|
|
75
|
+
const templatePath = path.join(TEMPLATES_DIR, templateName, 'src_tenant.sh');
|
|
76
|
+
if (fs.existsSync(templatePath)) return templatePath;
|
|
77
|
+
if (templateName === 'alpha' && fs.existsSync(LEGACY_ALPHA_DNA_PATH)) return LEGACY_ALPHA_DNA_PATH;
|
|
78
|
+
return templatePath;
|
|
79
|
+
}
|
|
80
|
+
|
|
78
81
|
program
|
|
79
82
|
.command('new')
|
|
80
83
|
.argument('<type>', 'Type of artifact (tenant)')
|
|
81
84
|
.argument('<name>', 'Name of the new tenant')
|
|
85
|
+
.option('--template <name>', 'Template profile (default: alpha)', 'alpha')
|
|
86
|
+
.option('--agritourism', 'Alias for --template agritourism')
|
|
82
87
|
.option('--script <path>', 'Override default deterministic script path')
|
|
83
88
|
.action(async (type, name, options) => {
|
|
84
89
|
if (type !== 'tenant') {
|
|
85
|
-
console.log(chalk.red('
|
|
90
|
+
console.log(chalk.red('Error: Only "tenant" type is supported.'));
|
|
86
91
|
return;
|
|
87
92
|
}
|
|
88
93
|
|
|
89
94
|
const targetDir = path.join(process.cwd(), name);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
const availableTemplates = getAvailableTemplates();
|
|
96
|
+
const template = options.agritourism ? 'agritourism' : options.template;
|
|
97
|
+
const scriptPath = options.script
|
|
98
|
+
? path.resolve(process.cwd(), options.script)
|
|
99
|
+
: resolveTemplateScriptPath(template);
|
|
100
|
+
|
|
101
|
+
if (!options.script && !availableTemplates.includes(template) && !(template === 'alpha' && fs.existsSync(LEGACY_ALPHA_DNA_PATH))) {
|
|
102
|
+
console.log(chalk.red(`Error: Unknown template "${template}".`));
|
|
103
|
+
console.log(chalk.yellow(`Available templates: ${availableTemplates.length ? availableTemplates.join(', ') : '(none found)'}`));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
95
106
|
|
|
96
107
|
if (!fs.existsSync(scriptPath)) {
|
|
97
|
-
console.log(chalk.red(
|
|
98
|
-
console.log(chalk.yellow(`Debug info:
|
|
108
|
+
console.log(chalk.red(`Error: DNA script not found at ${scriptPath}`));
|
|
109
|
+
console.log(chalk.yellow(`Debug info: template=${template}, assets=${CLI_ASSETS_DIR}`));
|
|
99
110
|
return;
|
|
100
111
|
}
|
|
101
112
|
|
|
102
|
-
console.log(chalk.blue.bold(`\
|
|
113
|
+
console.log(chalk.blue.bold(`\nProjecting Sovereign Tenant: ${name} (template: ${template})\n`));
|
|
103
114
|
const spinner = ora();
|
|
104
115
|
|
|
105
116
|
try {
|
|
106
|
-
// 1. SCAFFOLDING INFRA
|
|
107
117
|
spinner.start('Setting up environment (Vite + TS)...');
|
|
108
118
|
await fs.ensureDir(targetDir);
|
|
109
|
-
|
|
110
|
-
// Windows fix: npm.cmd invece di npm
|
|
119
|
+
|
|
111
120
|
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
112
|
-
|
|
113
121
|
await execa(npmCmd, ['create', 'vite@latest', '.', '--', '--template', 'react-ts'], { cwd: targetDir });
|
|
114
122
|
spinner.succeed('Environment scaffolded.');
|
|
115
123
|
|
|
116
|
-
// 2. CLEANUP
|
|
117
124
|
spinner.start('Wiping default boilerplate...');
|
|
118
125
|
await fs.emptyDir(path.join(targetDir, 'src'));
|
|
119
126
|
const junk = ['App.css', 'App.tsx', 'main.tsx', 'vite-env.d.ts', 'favicon.ico', 'index.html'];
|
|
@@ -123,28 +130,23 @@ program
|
|
|
123
130
|
}
|
|
124
131
|
spinner.succeed('Clean slate achieved.');
|
|
125
132
|
|
|
126
|
-
// 3. INJECTION
|
|
127
133
|
spinner.start('Injecting Sovereign Configurations...');
|
|
128
134
|
await injectInfraFiles(targetDir, name);
|
|
129
135
|
spinner.succeed('Infrastructure configured.');
|
|
130
136
|
|
|
131
|
-
// 4. DETERMINISTIC PROJECTION (Node-based Interpreter)
|
|
132
137
|
spinner.start('Executing deterministic src projection...');
|
|
133
|
-
// Invece di execa('./script.sh'), usiamo il nostro interprete
|
|
134
138
|
await processScriptInNode(scriptPath, targetDir);
|
|
135
139
|
spinner.succeed('Source code and assets projected successfully.');
|
|
136
140
|
|
|
137
|
-
// 5. Install dependencies (lo script .sh deve aver già scritto/copiato package.json in targetDir)
|
|
138
141
|
spinner.start('Installing dependencies (this may take a minute)...');
|
|
139
142
|
await execa(npmCmd, ['install'], { cwd: targetDir });
|
|
140
|
-
spinner.succeed(chalk.green.bold('
|
|
143
|
+
spinner.succeed(chalk.green.bold('Tenant Ready.'));
|
|
141
144
|
|
|
142
145
|
console.log(`\n${chalk.white.bgBlue(' NEXT STEPS ')}`);
|
|
143
146
|
console.log(` ${chalk.cyan(`cd ${name}`)}`);
|
|
144
|
-
console.log(` ${chalk.cyan(
|
|
145
|
-
console.log(` ${chalk.cyan(
|
|
146
|
-
console.log(`\
|
|
147
|
-
|
|
147
|
+
console.log(` ${chalk.cyan('npm run dev')} <- Start development`);
|
|
148
|
+
console.log(` ${chalk.cyan('npm run build')} <- Validate Green Build`);
|
|
149
|
+
console.log(`\nTemplate used: ${template}\n`);
|
|
148
150
|
} catch (error) {
|
|
149
151
|
spinner.fail(chalk.red('Projection failed.'));
|
|
150
152
|
console.error(error);
|
|
@@ -153,15 +155,15 @@ program
|
|
|
153
155
|
|
|
154
156
|
async function injectInfraFiles(targetDir, name) {
|
|
155
157
|
const pkg = {
|
|
156
|
-
name
|
|
158
|
+
name,
|
|
157
159
|
private: true,
|
|
158
|
-
version:
|
|
159
|
-
type:
|
|
160
|
+
version: '1.0.0',
|
|
161
|
+
type: 'module',
|
|
160
162
|
scripts: {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
163
|
+
dev: 'vite',
|
|
164
|
+
build: 'tsc && vite build',
|
|
165
|
+
preview: 'vite preview',
|
|
166
|
+
},
|
|
165
167
|
};
|
|
166
168
|
await fs.writeJson(path.join(targetDir, 'package.json'), pkg, { spaces: 2 });
|
|
167
169
|
|
|
@@ -197,26 +199,26 @@ async function injectInfraFiles(targetDir, name) {
|
|
|
197
199
|
await fs.writeFile(path.join(targetDir, 'tsconfig.json'), tsConfig.trim());
|
|
198
200
|
|
|
199
201
|
const shadcnConfig = {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
202
|
+
$schema: 'https://ui.shadcn.com/schema.json',
|
|
203
|
+
style: 'radix-nova',
|
|
204
|
+
rsc: false,
|
|
205
|
+
tsx: true,
|
|
206
|
+
tailwind: {
|
|
207
|
+
config: '',
|
|
208
|
+
css: 'src/index.css',
|
|
209
|
+
baseColor: 'zinc',
|
|
210
|
+
cssVariables: true,
|
|
211
|
+
prefix: '',
|
|
212
|
+
},
|
|
213
|
+
aliases: {
|
|
214
|
+
components: '@/components',
|
|
215
|
+
utils: '@/lib/utils',
|
|
216
|
+
ui: '@/components/ui',
|
|
217
|
+
lib: '@/lib',
|
|
218
|
+
hooks: '@/hooks',
|
|
210
219
|
},
|
|
211
|
-
"aliases": {
|
|
212
|
-
"components": "@/components",
|
|
213
|
-
"utils": "@/lib/utils",
|
|
214
|
-
"ui": "@/components/ui",
|
|
215
|
-
"lib": "@/lib",
|
|
216
|
-
"hooks": "@/hooks"
|
|
217
|
-
}
|
|
218
220
|
};
|
|
219
221
|
await fs.writeJson(path.join(targetDir, 'components.json'), shadcnConfig, { spaces: 2 });
|
|
220
222
|
}
|
|
221
223
|
|
|
222
|
-
program.parse();
|
|
224
|
+
program.parse();
|