@codemoreira/esad 1.1.3 → 1.2.1
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/bin/esad.js +35 -10
- package/package.json +2 -2
- package/src/cli/commands/createModule.js +11 -0
- package/src/cli/commands/deploy.js +15 -4
- package/src/cli/commands/dev.js +26 -5
- package/src/cli/commands/host.js +150 -0
- package/src/cli/commands/init.js +22 -2
- package/src/cli/templates/host.js +3 -1
package/bin/esad.js
CHANGED
|
@@ -8,44 +8,69 @@ const createModuleCommand = require('../src/cli/commands/createModule');
|
|
|
8
8
|
const createCdnCommand = require('../src/cli/commands/createCdn');
|
|
9
9
|
const deployCommand = require('../src/cli/commands/deploy');
|
|
10
10
|
const devCommand = require('../src/cli/commands/dev');
|
|
11
|
+
const hostCommand = require('../src/cli/commands/host');
|
|
11
12
|
|
|
12
13
|
program
|
|
13
|
-
.version('1.
|
|
14
|
+
.version('1.2.0')
|
|
14
15
|
.description('esad - Easy Super App Development Toolkit');
|
|
15
16
|
|
|
16
17
|
// --- COMMMAND: esad init ---
|
|
17
18
|
program
|
|
18
19
|
.command('init <project-name>')
|
|
19
20
|
.description('Scaffold a new ESAD workspace containing the Host App')
|
|
20
|
-
.action(
|
|
21
|
+
.action(async (name) => {
|
|
22
|
+
await initCommand(name);
|
|
23
|
+
process.exit(0);
|
|
24
|
+
});
|
|
21
25
|
|
|
22
26
|
// --- COMMAND: esad create-cdn ---
|
|
23
27
|
program
|
|
24
28
|
.command('create-cdn [cdn-name]')
|
|
25
29
|
.description('Scaffold the CDN / Registry backend')
|
|
26
|
-
.action(
|
|
30
|
+
.action(async (name) => {
|
|
31
|
+
await createCdnCommand(name);
|
|
32
|
+
process.exit(0);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// --- COMMAND: esad host ---
|
|
36
|
+
program
|
|
37
|
+
.command('host <subcommand>')
|
|
38
|
+
.description('Manage the Host App (dev, android, ios)')
|
|
39
|
+
.action(async (sub) => {
|
|
40
|
+
await hostCommand(sub);
|
|
41
|
+
process.exit(0);
|
|
42
|
+
});
|
|
27
43
|
|
|
28
44
|
// --- COMMAND: esad create-module ---
|
|
29
45
|
program
|
|
30
46
|
.command('create-module <module-name>')
|
|
31
47
|
.description('Scaffold a React Native mini-app automatically configured for Module Federation via ESAD')
|
|
32
|
-
.action(
|
|
48
|
+
.action(async (name) => {
|
|
49
|
+
await createModuleCommand(name);
|
|
50
|
+
process.exit(0);
|
|
51
|
+
});
|
|
33
52
|
|
|
34
53
|
// --- COMMAND: esad deploy ---
|
|
35
54
|
program
|
|
36
55
|
.command('deploy')
|
|
37
|
-
.
|
|
38
|
-
.
|
|
39
|
-
.
|
|
56
|
+
.option('-v, --version <semver>', 'Version number (e.g., 1.0.0)')
|
|
57
|
+
.option('-i, --id <moduleId>', 'The Module ID to deploy')
|
|
58
|
+
.option('-e, --entry <entryFileName>', 'The name of the main entry bundle (e.g., index.bundle)', 'index.bundle')
|
|
40
59
|
.description('Zips the local dist directory and uploads it to the configured deployment endpoint')
|
|
41
|
-
.action(
|
|
60
|
+
.action(async (options) => {
|
|
61
|
+
await deployCommand(options);
|
|
62
|
+
process.exit(0);
|
|
63
|
+
});
|
|
42
64
|
|
|
43
65
|
// --- COMMAND: esad dev ---
|
|
44
66
|
program
|
|
45
67
|
.command('dev')
|
|
46
|
-
.
|
|
68
|
+
.option('-i, --id <moduleId>', 'The Module ID to run in dev mode')
|
|
47
69
|
.option('-p, --port <port>', 'The port to run the dev server on', '8081')
|
|
48
70
|
.description('Starts the dev server and updates the external registry to bypass CDN')
|
|
49
|
-
.action(
|
|
71
|
+
.action(async (options) => {
|
|
72
|
+
await devCommand(options);
|
|
73
|
+
// Note: dev command has its own shutdown logic with SIGINT/SIGTERM
|
|
74
|
+
});
|
|
50
75
|
|
|
51
76
|
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codemoreira/esad",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Easy Super App Development - Zero-Config CLI and DevTools for React Native Module Federation",
|
|
5
5
|
"main": "src/plugin/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -27,4 +27,4 @@
|
|
|
27
27
|
"fs-extra": "^11.2.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {}
|
|
30
|
-
}
|
|
30
|
+
}
|
|
@@ -29,6 +29,17 @@ module.exports = async (moduleName) => {
|
|
|
29
29
|
fs.writeFileSync(path.join(targetDir, 'rspack.config.mjs'), rspackContent);
|
|
30
30
|
console.log(`✅ Injected withESAD wrapper into rspack.config.mjs`);
|
|
31
31
|
|
|
32
|
+
// Update package.json scripts
|
|
33
|
+
const modPkgPath = path.join(targetDir, 'package.json');
|
|
34
|
+
const modPkg = fs.readJsonSync(modPkgPath);
|
|
35
|
+
modPkg.scripts = {
|
|
36
|
+
...modPkg.scripts,
|
|
37
|
+
"start": "esad dev",
|
|
38
|
+
"deploy": "esad deploy"
|
|
39
|
+
};
|
|
40
|
+
fs.writeJsonSync(modPkgPath, modPkg, { spaces: 2 });
|
|
41
|
+
console.log(`✅ Abstracted module scripts to use ESAD CLI.`);
|
|
42
|
+
|
|
32
43
|
console.log(`\n🎉 Module ${finalModuleName} is ready!`);
|
|
33
44
|
} catch (err) {
|
|
34
45
|
console.error(`❌ Failed to scaffold module`, err.message);
|
|
@@ -4,7 +4,18 @@ const AdmZip = require('adm-zip');
|
|
|
4
4
|
const { getWorkspaceConfig } = require('../utils/config');
|
|
5
5
|
|
|
6
6
|
module.exports = async (options) => {
|
|
7
|
-
|
|
7
|
+
const pkgPath = path.join(process.cwd(), 'package.json');
|
|
8
|
+
if (!fs.existsSync(pkgPath)) {
|
|
9
|
+
console.error(`❌ Error: package.json not found.`);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const pkg = fs.readJsonSync(pkgPath);
|
|
14
|
+
const moduleId = options.id || pkg.name;
|
|
15
|
+
const version = options.version || pkg.version;
|
|
16
|
+
const entry = options.entry || 'index.bundle';
|
|
17
|
+
|
|
18
|
+
console.log(`\n☁️ Starting ESAD Deploy for ${moduleId} (v${version})\n`);
|
|
8
19
|
|
|
9
20
|
const configObj = getWorkspaceConfig();
|
|
10
21
|
if (!configObj) {
|
|
@@ -18,7 +29,7 @@ module.exports = async (options) => {
|
|
|
18
29
|
process.exit(1);
|
|
19
30
|
}
|
|
20
31
|
|
|
21
|
-
const deployUrl = config.deployEndpoint.replace('{{moduleId}}',
|
|
32
|
+
const deployUrl = config.deployEndpoint.replace('{{moduleId}}', moduleId);
|
|
22
33
|
console.log(`📡 Deployment Endpoint Resolved: ${deployUrl}`);
|
|
23
34
|
|
|
24
35
|
const distPath = path.join(process.cwd(), 'dist');
|
|
@@ -30,12 +41,12 @@ module.exports = async (options) => {
|
|
|
30
41
|
const zip = new AdmZip();
|
|
31
42
|
zip.addLocalFolder(distPath);
|
|
32
43
|
|
|
33
|
-
const zipPath = path.join(process.cwd(), `bundle-${
|
|
44
|
+
const zipPath = path.join(process.cwd(), `bundle-${moduleId}-${version}.zip`);
|
|
34
45
|
zip.writeZip(zipPath);
|
|
35
46
|
console.log(`🗜️ Zipped output to ${zipPath}`);
|
|
36
47
|
|
|
37
48
|
console.log(`🚀 Uploading to CDN via multipart POST...`);
|
|
38
|
-
//
|
|
49
|
+
// Note: Here we would use form-data + fetch or axios to upload to Simple CDN
|
|
39
50
|
|
|
40
51
|
console.log(`✅ [SIMULATED] Successfully uploaded to ${deployUrl}`);
|
|
41
52
|
fs.unlinkSync(zipPath);
|
package/src/cli/commands/dev.js
CHANGED
|
@@ -1,28 +1,49 @@
|
|
|
1
1
|
const { spawn } = require('cross-spawn');
|
|
2
2
|
const { getWorkspaceConfig } = require('../utils/config');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
3
5
|
|
|
4
6
|
module.exports = async (options) => {
|
|
5
|
-
|
|
7
|
+
const pkgPath = path.join(process.cwd(), 'package.json');
|
|
8
|
+
if (!fs.existsSync(pkgPath)) {
|
|
9
|
+
console.error(`❌ Error: Call this command from inside a Host or Module directory.`);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const pkg = fs.readJsonSync(pkgPath);
|
|
14
|
+
const moduleId = options.id || pkg.name;
|
|
15
|
+
const port = options.port || '8081';
|
|
16
|
+
|
|
17
|
+
// Determine if it's a Host or Module
|
|
18
|
+
const isHost = pkg.name.endsWith('-host') || pkg.dependencies?.['@callstack/repack'];
|
|
19
|
+
|
|
20
|
+
if (isHost && !options.id) {
|
|
21
|
+
console.log(`\n🚀 Starting Host App Dev Server (Re.Pack/Rspack)...\n`);
|
|
22
|
+
await spawn('npx', ['react-native', 'webpack-start'], { stdio: 'inherit', shell: true });
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log(`\n⚡ Starting ESAD Dev Server for ${moduleId} on port ${port}...\n`);
|
|
6
27
|
|
|
7
28
|
const configObj = getWorkspaceConfig();
|
|
8
29
|
const config = configObj ? configObj.data : null;
|
|
9
|
-
let devApiUrl = config?.devModeEndpoint ? config.devModeEndpoint.replace('{{moduleId}}',
|
|
30
|
+
let devApiUrl = config?.devModeEndpoint ? config.devModeEndpoint.replace('{{moduleId}}', moduleId) : null;
|
|
10
31
|
|
|
11
32
|
const setDevMode = async (isActive) => {
|
|
12
33
|
if (!devApiUrl) return;
|
|
13
34
|
try {
|
|
14
35
|
const body = {
|
|
15
36
|
is_dev_mode: isActive,
|
|
16
|
-
...(isActive && { dev_url: `http://localhost:${
|
|
37
|
+
...(isActive && { dev_url: `http://localhost:${port}/index.bundle` })
|
|
17
38
|
};
|
|
18
39
|
const res = await fetch(devApiUrl, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
|
|
19
|
-
if (res.ok) console.log(`📡 Registry Sync: Dev Override is ${isActive ? 'ON' : 'OFF'} para o modulo
|
|
40
|
+
if (res.ok) console.log(`📡 Registry Sync: Dev Override is ${isActive ? 'ON' : 'OFF'} para o modulo ${moduleId}`);
|
|
20
41
|
} catch(e) { /* ignore */ }
|
|
21
42
|
};
|
|
22
43
|
|
|
23
44
|
await setDevMode(true);
|
|
24
45
|
|
|
25
|
-
const proc = spawn('npx', ['react-native', 'webpack-start', '--port',
|
|
46
|
+
const proc = spawn('npx', ['react-native', 'webpack-start', '--port', port], { stdio: 'inherit', shell: true });
|
|
26
47
|
|
|
27
48
|
const shutdown = async () => {
|
|
28
49
|
console.log(`\n🛑 Parando ESAD Dev Server e revertendo o registro na CDN...`);
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
const { runProcess } = require('../utils/process');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const { spawn } = require('cross-spawn');
|
|
5
|
+
const http = require('http');
|
|
6
|
+
const readline = require('readline');
|
|
7
|
+
|
|
8
|
+
const rl = readline.createInterface({
|
|
9
|
+
input: process.stdin,
|
|
10
|
+
output: process.stdout
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
14
|
+
|
|
15
|
+
module.exports = async (subcommand) => {
|
|
16
|
+
const cwd = process.cwd();
|
|
17
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
18
|
+
|
|
19
|
+
if (!fs.existsSync(pkgPath)) {
|
|
20
|
+
console.error(`❌ Error: Call this command from inside the Host App directory.`);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const pkg = fs.readJsonSync(pkgPath);
|
|
25
|
+
|
|
26
|
+
// 1. Initial Checks & Prebuild
|
|
27
|
+
if (subcommand === 'dev' || subcommand === 'start') {
|
|
28
|
+
if (!fs.existsSync(path.join(cwd, 'android')) && !fs.existsSync(path.join(cwd, 'ios'))) {
|
|
29
|
+
console.log(`📦 Native folders not found. Running expo prebuild...`);
|
|
30
|
+
await runProcess('npx', ['expo', 'prebuild'], cwd);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 2. Patch Native Files
|
|
34
|
+
console.log(`🔧 Patching native files for Re.Pack compatibility...`);
|
|
35
|
+
const patchFiles = async () => {
|
|
36
|
+
// Android
|
|
37
|
+
const androidMainApp = path.join(cwd, 'android/app/src/main/java');
|
|
38
|
+
if (fs.existsSync(androidMainApp)) {
|
|
39
|
+
const files = await fs.readdir(androidMainApp, { recursive: true });
|
|
40
|
+
for (const file of files) {
|
|
41
|
+
if (file.endsWith('MainApplication.kt') || file.endsWith('MainApplication.java')) {
|
|
42
|
+
const filePath = path.join(androidMainApp, file);
|
|
43
|
+
let content = await fs.readFile(filePath, 'utf8');
|
|
44
|
+
if (content.includes('.expo/.virtual-metro-entry')) {
|
|
45
|
+
content = content.replace(/.expo\/.virtual-metro-entry/g, 'index');
|
|
46
|
+
await fs.writeFile(filePath, content);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// iOS
|
|
52
|
+
const iosDir = path.join(cwd, 'ios');
|
|
53
|
+
if (fs.existsSync(iosDir)) {
|
|
54
|
+
const iosFiles = await fs.readdir(iosDir, { recursive: true });
|
|
55
|
+
for (const file of iosFiles) {
|
|
56
|
+
if (file.match(/AppDelegate\.(m|mm|swift)/)) {
|
|
57
|
+
const filePath = path.join(iosDir, file);
|
|
58
|
+
let content = await fs.readFile(filePath, 'utf8');
|
|
59
|
+
if (content.includes('.expo/.virtual-metro-entry')) {
|
|
60
|
+
content = content.replace(/.expo\/.virtual-metro-entry/g, 'index');
|
|
61
|
+
await fs.writeFile(filePath, content);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
await patchFiles();
|
|
68
|
+
|
|
69
|
+
// 3. Platform Selection
|
|
70
|
+
console.log(`\nESAD Host Dev Manager`);
|
|
71
|
+
console.log(`---------------------`);
|
|
72
|
+
console.log(`[a] Run on Android`);
|
|
73
|
+
console.log(`[i] Run on iOS`);
|
|
74
|
+
console.log(`[b] Bundler Only`);
|
|
75
|
+
console.log(`[c] Cancel`);
|
|
76
|
+
|
|
77
|
+
const choice = (await askQuestion(`\nSelect platform: `)).toLowerCase();
|
|
78
|
+
|
|
79
|
+
if (choice === 'c') {
|
|
80
|
+
console.log(`\n❌ Cancelled.`);
|
|
81
|
+
rl.close();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 4. Start Bundler in a New Window
|
|
86
|
+
console.log(`\n🛠️ Starting Rspack Bundler in a new window...`);
|
|
87
|
+
if (process.platform === 'win32') {
|
|
88
|
+
// Use CMD /C START to open a new window
|
|
89
|
+
spawn('cmd', ['/c', 'start', 'npx', 'react-native', 'webpack-start'], {
|
|
90
|
+
detached: true,
|
|
91
|
+
stdio: 'ignore',
|
|
92
|
+
shell: true
|
|
93
|
+
}).unref();
|
|
94
|
+
} else {
|
|
95
|
+
// For MacOS or Linux
|
|
96
|
+
spawn('npx', ['react-native', 'webpack-start'], { detached: true, stdio: 'inherit', shell: true }).unref();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 5. Wait for Bundler (Port 8081)
|
|
100
|
+
console.log(`⏳ Waiting for Rspack Bundler to initialize on port 8081...`);
|
|
101
|
+
const waitForBundler = async () => {
|
|
102
|
+
for (let i = 0; i < 30; i++) {
|
|
103
|
+
try {
|
|
104
|
+
await new Promise((resolve, reject) => {
|
|
105
|
+
const req = http.get('http://localhost:8081', (res) => resolve(res));
|
|
106
|
+
req.on('error', reject);
|
|
107
|
+
req.end();
|
|
108
|
+
});
|
|
109
|
+
return true;
|
|
110
|
+
} catch (e) {
|
|
111
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const isReady = await waitForBundler();
|
|
118
|
+
if (!isReady) {
|
|
119
|
+
console.error(`\n❌ Timeout: Bundler did not respond after 60 seconds.`);
|
|
120
|
+
rl.close();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
console.log(`✅ Bundler stable and ready to use!`);
|
|
124
|
+
|
|
125
|
+
// 6. Launch Native App
|
|
126
|
+
if (choice === 'a') {
|
|
127
|
+
console.log(`🤖 Compiling and sending to Android...`);
|
|
128
|
+
await runProcess('npx', ['expo', 'run:android', '--no-bundler'], cwd);
|
|
129
|
+
} else if (choice === 'i') {
|
|
130
|
+
console.log(`🍎 Compiling and sending to iOS...`);
|
|
131
|
+
await runProcess('npx', ['expo', 'run:ios', '--no-bundler'], cwd);
|
|
132
|
+
} else if (choice === 'b') {
|
|
133
|
+
console.log(`✨ Bundler is running. You can open the app manually.`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
rl.close();
|
|
137
|
+
} else {
|
|
138
|
+
// Other subcommands (android, ios directly)
|
|
139
|
+
try {
|
|
140
|
+
if (subcommand === 'android') {
|
|
141
|
+
await runProcess('npx', ['expo', 'run:android', '--no-bundler'], cwd);
|
|
142
|
+
} else if (subcommand === 'ios') {
|
|
143
|
+
await runProcess('npx', ['expo', 'run:ios', '--no-bundler'], cwd);
|
|
144
|
+
}
|
|
145
|
+
} catch (err) {
|
|
146
|
+
console.error(`❌ Error running host command: ${err.message}`);
|
|
147
|
+
}
|
|
148
|
+
rl.close();
|
|
149
|
+
}
|
|
150
|
+
};
|
package/src/cli/commands/init.js
CHANGED
|
@@ -44,12 +44,17 @@ module.exports = async (projectName) => {
|
|
|
44
44
|
console.log(`\n📦 Scaffolding clean Expo project: ${hostName}...`);
|
|
45
45
|
await runProcess('npx', ['create-expo-app', hostName, '--template', 'blank'], workspaceDir);
|
|
46
46
|
|
|
47
|
-
console.log(`\n📦 Installing ESAD and UI dependencies into host...`);
|
|
48
|
-
const
|
|
47
|
+
console.log(`\n📦 Installing ESAD, Re.Pack and UI dependencies into host...`);
|
|
48
|
+
const hostPkgPath = path.join(hostDir, 'package.json');
|
|
49
|
+
const hostPkg = fs.readJsonSync(hostPkgPath);
|
|
49
50
|
const reactVersion = hostPkg.dependencies.react;
|
|
50
51
|
|
|
51
52
|
const deps = [
|
|
52
53
|
'@codemoreira/esad',
|
|
54
|
+
'@callstack/repack@^5.2.5',
|
|
55
|
+
'@rspack/core@^1.7.9',
|
|
56
|
+
'@rspack/plugin-react-refresh@^1.6.1',
|
|
57
|
+
'@callstack/repack-plugin-expo-modules',
|
|
53
58
|
'nativewind',
|
|
54
59
|
'tailwindcss',
|
|
55
60
|
'postcss',
|
|
@@ -63,6 +68,20 @@ module.exports = async (projectName) => {
|
|
|
63
68
|
];
|
|
64
69
|
await runProcess('npm', ['install', ...deps], hostDir);
|
|
65
70
|
|
|
71
|
+
// Re-read package.json to get the version after npm install updated it
|
|
72
|
+
const updatedPkg = fs.readJsonSync(hostPkgPath);
|
|
73
|
+
|
|
74
|
+
// Update package.json scripts to delegate to ESAD CLI
|
|
75
|
+
updatedPkg.scripts = {
|
|
76
|
+
...updatedPkg.scripts,
|
|
77
|
+
"start": "esad host start",
|
|
78
|
+
"android": "esad host android",
|
|
79
|
+
"ios": "esad host ios",
|
|
80
|
+
"dev": "esad host dev"
|
|
81
|
+
};
|
|
82
|
+
fs.writeJsonSync(hostPkgPath, updatedPkg, { spaces: 2 });
|
|
83
|
+
console.log(`✅ Abstracted package.json scripts to use ESAD CLI.`);
|
|
84
|
+
|
|
66
85
|
console.log(`\n🎨 Configuring NativeWind & Tailwind...`);
|
|
67
86
|
fs.writeFileSync(path.join(hostDir, 'tailwind.config.js'), templates.tailwindConfig);
|
|
68
87
|
fs.writeFileSync(path.join(hostDir, 'babel.config.js'), templates.babelConfig);
|
|
@@ -81,6 +100,7 @@ module.exports = async (projectName) => {
|
|
|
81
100
|
fs.writeFileSync(path.join(hostDir, 'app/login.tsx'), templates.loginPage);
|
|
82
101
|
fs.writeFileSync(path.join(hostDir, 'app/global.css'), templates.globalCss);
|
|
83
102
|
fs.writeFileSync(path.join(hostDir, 'lib/moduleLoader.ts'), templates.moduleLoader);
|
|
103
|
+
fs.writeFileSync(path.join(hostDir, 'index.js'), templates.indexJs);
|
|
84
104
|
fs.writeFileSync(path.join(hostDir, 'app/(protected)/index.tsx'), templates.dashboard);
|
|
85
105
|
fs.writeFileSync(path.join(hostDir, 'app/(protected)/module/[id].tsx'), templates.modulePage);
|
|
86
106
|
|