create-medan-app 0.1.0 → 0.1.2
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/dist/index.js +6 -3
- package/dist/templates.js +156 -33
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { existsSync, mkdirSync, readdirSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
|
-
import { GITIGNORE,
|
|
4
|
+
import { DEV_MJS, GITIGNORE, mainMedan, packageJson, readme } from "./templates.js";
|
|
5
5
|
function isValidPackageName(name) {
|
|
6
6
|
return /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(name);
|
|
7
7
|
}
|
|
@@ -22,13 +22,16 @@ function main() {
|
|
|
22
22
|
}
|
|
23
23
|
mkdirSync(targetDir, { recursive: true });
|
|
24
24
|
writeFileSync(resolve(targetDir, "package.json"), packageJson(rawName));
|
|
25
|
-
writeFileSync(resolve(targetDir, "main.medan"),
|
|
25
|
+
writeFileSync(resolve(targetDir, "main.medan"), mainMedan(rawName));
|
|
26
|
+
writeFileSync(resolve(targetDir, "dev.mjs"), DEV_MJS);
|
|
26
27
|
writeFileSync(resolve(targetDir, "README.md"), readme(rawName));
|
|
27
28
|
writeFileSync(resolve(targetDir, ".gitignore"), GITIGNORE);
|
|
28
29
|
console.log(`mantap kali! project '${rawName}' udah jadi.\n`);
|
|
29
30
|
console.log("langkah selanjutnya:\n");
|
|
30
31
|
console.log(` cd ${rawName}`);
|
|
31
32
|
console.log(" npm install");
|
|
32
|
-
console.log(" npm
|
|
33
|
+
console.log(" npm run dev # auto-regenerate tiap main.medan disave");
|
|
34
|
+
console.log(" # atau: npm start # generate sekali doang");
|
|
35
|
+
console.log(" # terus buka landing.html di browser");
|
|
33
36
|
}
|
|
34
37
|
main();
|
package/dist/templates.js
CHANGED
|
@@ -4,54 +4,170 @@ export function packageJson(name) {
|
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"private": true,
|
|
6
6
|
"scripts": {
|
|
7
|
-
"start": "medan main.medan"
|
|
7
|
+
"start": "medan main.medan",
|
|
8
|
+
"dev": "node dev.mjs"
|
|
8
9
|
},
|
|
9
10
|
"dependencies": {
|
|
10
|
-
"medan-language": "^0.1.
|
|
11
|
+
"medan-language": "^0.1.3"
|
|
11
12
|
}
|
|
12
13
|
}
|
|
13
14
|
`;
|
|
14
15
|
}
|
|
15
|
-
export const
|
|
16
|
-
// jalanin
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
16
|
+
export const DEV_MJS = `// dev.mjs
|
|
17
|
+
// "npm run dev" jalanin file ini: generate landing.html sekali, terus
|
|
18
|
+
// nungguin main.medan berubah dan regenerate otomatis tiap disave.
|
|
19
|
+
|
|
20
|
+
import { spawnSync } from "node:child_process";
|
|
21
|
+
import { watch } from "node:fs";
|
|
22
|
+
|
|
23
|
+
function build() {
|
|
24
|
+
console.log("\\n[medan] generate landing.html ...");
|
|
25
|
+
const result = spawnSync("medan", ["main.medan"], { stdio: "inherit" });
|
|
26
|
+
if (result.status !== 0) {
|
|
27
|
+
console.error("[medan] ada error di atas, benerin dulu terus save lagi.");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
build();
|
|
32
|
+
console.log("[medan] mantau main.medan, ctrl+c buat berhenti...");
|
|
33
|
+
|
|
34
|
+
let timer;
|
|
35
|
+
watch("main.medan", () => {
|
|
36
|
+
clearTimeout(timer);
|
|
37
|
+
timer = setTimeout(build, 100);
|
|
38
|
+
});
|
|
39
|
+
`;
|
|
40
|
+
export function mainMedan(name) {
|
|
41
|
+
return `// main.medan
|
|
42
|
+
// Ini titik masuk project "${name}" kamu.
|
|
43
|
+
// "npm start" jalanin file ini, yang nge-generate landing.html.
|
|
44
|
+
// Edit di bawah, jalanin lagi "npm start", terus refresh landing.html di browser.
|
|
45
|
+
|
|
46
|
+
bikin css()
|
|
47
|
+
balikin "
|
|
48
|
+
<style>
|
|
49
|
+
:root {
|
|
50
|
+
--bg: #1B120D;
|
|
51
|
+
--bg-2: #241A13;
|
|
52
|
+
--bg-3: #2E2117;
|
|
53
|
+
--ink: #F5EDE2;
|
|
54
|
+
--ink-dim: #C9B8A6;
|
|
55
|
+
--chili: #E4402A;
|
|
56
|
+
--chili-ink: #FFEDE9;
|
|
57
|
+
--turmeric: #F2B705;
|
|
58
|
+
--daun: #7FAE84;
|
|
59
|
+
--line: rgba(245,237,226,0.14);
|
|
60
|
+
--shadow: rgba(10,6,3,0.5);
|
|
61
|
+
}
|
|
62
|
+
@media (prefers-color-scheme: light) {
|
|
63
|
+
:root:not([data-theme='dark']) {
|
|
64
|
+
--bg: #F7EEDD; --bg-2: #FFFAF0; --bg-3: #FDF1DA;
|
|
65
|
+
--ink: #2A1B12; --ink-dim: #6B5645;
|
|
66
|
+
--chili: #C7301C; --chili-ink: #FFF6F4; --turmeric: #93690A; --daun: #3E6B45;
|
|
67
|
+
--line: rgba(42,27,18,0.14); --shadow: rgba(42,27,18,0.16);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
:root[data-theme='light'] {
|
|
71
|
+
--bg: #F7EEDD; --bg-2: #FFFAF0; --bg-3: #FDF1DA;
|
|
72
|
+
--ink: #2A1B12; --ink-dim: #6B5645;
|
|
73
|
+
--chili: #C7301C; --chili-ink: #FFF6F4; --turmeric: #93690A; --daun: #3E6B45;
|
|
74
|
+
--line: rgba(42,27,18,0.14); --shadow: rgba(42,27,18,0.16);
|
|
75
|
+
}
|
|
76
|
+
* { box-sizing: border-box; }
|
|
77
|
+
body { margin: 0; background: var(--bg); color: var(--ink); font-family: 'Plus Jakarta Sans', system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
|
|
78
|
+
.page { max-width: 880px; margin: 0 auto; padding: 0 24px 80px; }
|
|
79
|
+
code, .mono { font-family: 'JetBrains Mono', ui-monospace, monospace; }
|
|
80
|
+
h1, h2 { font-family: 'Alfa Slab One', Georgia, serif; font-weight: 400; text-wrap: balance; margin: 0; }
|
|
81
|
+
.eyebrow { font-family: 'JetBrains Mono', monospace; text-transform: uppercase; letter-spacing: 0.16em; font-size: 12px; color: var(--turmeric); margin: 0 0 12px; }
|
|
82
|
+
.hero { padding: 72px 0 40px; }
|
|
83
|
+
h1.headline { font-size: clamp(34px, 6vw, 58px); line-height: 1.02; text-shadow: 3px 3px 0 var(--shadow); }
|
|
84
|
+
h1.headline span { color: var(--chili); }
|
|
85
|
+
.lede { max-width: 60ch; font-size: 17px; line-height: 1.6; color: var(--ink-dim); margin: 18px 0 0; }
|
|
86
|
+
section { padding: 44px 0; border-top: 1px solid var(--line); }
|
|
87
|
+
section h2 { font-size: clamp(22px, 3.4vw, 30px); margin-bottom: 20px; }
|
|
88
|
+
.step-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 14px; }
|
|
89
|
+
.step-list li { display: flex; gap: 16px; align-items: flex-start; background: var(--bg-2); border: 1px solid var(--line); border-radius: 10px; padding: 16px 18px; }
|
|
90
|
+
.step-no { flex: none; width: 30px; height: 30px; border-radius: 50%; background: var(--turmeric); color: var(--bg); font-family: 'JetBrains Mono', monospace; font-weight: 700; display: flex; align-items: center; justify-content: center; font-size: 14px; }
|
|
91
|
+
.step-list strong { display: block; margin-bottom: 4px; font-size: 14.5px; }
|
|
92
|
+
.step-list code { display: inline-block; background: var(--bg-3); padding: 3px 7px; border-radius: 5px; font-size: 12.5px; }
|
|
93
|
+
.kw-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px; }
|
|
94
|
+
.kw-card { background: var(--bg-2); border: 1px solid var(--line); border-radius: 10px; padding: 16px; }
|
|
95
|
+
.kw-card h3 { font-family: 'JetBrains Mono', monospace; color: var(--chili); font-size: 15px; margin: 0 0 6px; }
|
|
96
|
+
.kw-card p { margin: 0; color: var(--ink-dim); font-size: 13px; }
|
|
97
|
+
.footer { color: var(--ink-dim); font-size: 13.5px; font-family: 'JetBrains Mono', monospace; }
|
|
98
|
+
.footer a { color: var(--turmeric); }
|
|
99
|
+
</style>
|
|
100
|
+
"
|
|
28
101
|
siap
|
|
29
102
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
103
|
+
modal fontLink = "<link rel='preconnect' href='https://fonts.googleapis.com'><link rel='stylesheet' href='https://fonts.googleapis.com/css2?family=Alfa+Slab+One&family=Plus+Jakarta+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap'>"
|
|
104
|
+
|
|
105
|
+
bikin hero()
|
|
106
|
+
balikin "
|
|
107
|
+
<header class='hero'>
|
|
108
|
+
<p class='eyebrow'>project baru, rasa Medan</p>
|
|
109
|
+
<h1 class='headline'>SELAMAT DATANG DI<br><span>${name}</span></h1>
|
|
110
|
+
<p class='lede'>Halaman ini di-generate langsung oleh main.medan pake Medan-lang. Edit file itu, jalanin \\"npm start\\" lagi, terus refresh halaman ini.</p>
|
|
111
|
+
</header>
|
|
112
|
+
"
|
|
33
113
|
siap
|
|
34
114
|
|
|
35
|
-
|
|
115
|
+
bikin stepItem(nomor, judul, kode)
|
|
116
|
+
balikin "<li><span class='step-no'>" + nomor + "</span><div><strong>" + judul + "</strong><code>" + kode + "</code></div></li>"
|
|
117
|
+
siap
|
|
118
|
+
|
|
119
|
+
bikin stepsSection()
|
|
120
|
+
modal items = []
|
|
121
|
+
tambahin(items, stepItem(1, "Edit kode kamu", "main.medan"))
|
|
122
|
+
tambahin(items, stepItem(2, "Generate ulang", "npm start"))
|
|
123
|
+
tambahin(items, stepItem(3, "Liat hasilnya", "buka landing.html di browser"))
|
|
124
|
+
|
|
125
|
+
balikin "
|
|
126
|
+
<section>
|
|
127
|
+
<h2>Mulai Dari Sini</h2>
|
|
128
|
+
<ol class='step-list'>" + gabung(items, "") + "</ol>
|
|
129
|
+
</section>
|
|
130
|
+
"
|
|
131
|
+
siap
|
|
36
132
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
gas i < 3
|
|
40
|
-
bilang "gas ke " + i
|
|
41
|
-
i = i + 1
|
|
133
|
+
bikin kwCard(nama, arti)
|
|
134
|
+
balikin "<div class='kw-card'><h3>" + nama + "</h3><p>" + arti + "</p></div>"
|
|
42
135
|
siap
|
|
43
136
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
137
|
+
bikin cheatsheetSection()
|
|
138
|
+
modal cards = []
|
|
139
|
+
tambahin(cards, kwCard("modal", "deklarasi variabel"))
|
|
140
|
+
tambahin(cards, kwCard("bilang", "cetak / print"))
|
|
141
|
+
tambahin(cards, kwCard("kalo / kalo gak", "if / else"))
|
|
142
|
+
tambahin(cards, kwCard("gas", "while"))
|
|
143
|
+
tambahin(cards, kwCard("ulang ... kali", "repeat N kali"))
|
|
144
|
+
tambahin(cards, kwCard("bikin / balikin", "fungsi / return"))
|
|
145
|
+
tambahin(cards, kwCard("mantap / lembek", "true / false"))
|
|
146
|
+
tambahin(cards, kwCard("kosong", "null"))
|
|
147
|
+
tambahin(cards, kwCard("[ ... ]", "array / list"))
|
|
148
|
+
|
|
149
|
+
balikin "
|
|
150
|
+
<section>
|
|
151
|
+
<h2>Contekan Keyword</h2>
|
|
152
|
+
<div class='kw-grid'>" + gabung(cards, "") + "</div>
|
|
153
|
+
</section>
|
|
154
|
+
"
|
|
47
155
|
siap
|
|
48
156
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
157
|
+
bikin footerSection()
|
|
158
|
+
balikin "
|
|
159
|
+
<footer class='footer'>
|
|
160
|
+
<p>Dibuat pake <a href='https://www.npmjs.com/package/create-medan-app'>create-medan-app</a> · powered by <a href='https://www.npmjs.com/package/medan-language'>medan-language</a></p>
|
|
161
|
+
</footer>
|
|
162
|
+
"
|
|
163
|
+
siap
|
|
164
|
+
|
|
165
|
+
modal halaman = "<title>${name}</title>" + fontLink + css() + "<div class='page'>" + hero() + stepsSection() + cheatsheetSection() + footerSection() + "</div>"
|
|
166
|
+
|
|
167
|
+
tulis("landing.html", halaman)
|
|
168
|
+
bilang "landing.html kelar digenerate. buka file itu di browser lu."
|
|
54
169
|
`;
|
|
170
|
+
}
|
|
55
171
|
export function readme(name) {
|
|
56
172
|
return `# ${name}
|
|
57
173
|
|
|
@@ -61,9 +177,15 @@ Project Medan-lang, di-scaffold pakai \`create-medan-app\`.
|
|
|
61
177
|
|
|
62
178
|
\`\`\`bash
|
|
63
179
|
npm install
|
|
64
|
-
npm
|
|
180
|
+
npm run dev
|
|
65
181
|
\`\`\`
|
|
66
182
|
|
|
183
|
+
\`npm run dev\` generate \`landing.html\` terus mantau \`main.medan\` — tiap kamu
|
|
184
|
+
save, otomatis di-generate ulang. Buka \`landing.html\` di browser, refresh
|
|
185
|
+
manual tiap abis edit & save.
|
|
186
|
+
|
|
187
|
+
Mau generate sekali doang tanpa watch mode, pakai \`npm start\`.
|
|
188
|
+
|
|
67
189
|
## Belajar lebih lanjut
|
|
68
190
|
|
|
69
191
|
Referensi lengkap bahasa Medan-lang (semua keyword, tipe data, contoh) ada
|
|
@@ -71,4 +193,5 @@ di: https://www.npmjs.com/package/medan-language
|
|
|
71
193
|
`;
|
|
72
194
|
}
|
|
73
195
|
export const GITIGNORE = `node_modules/
|
|
196
|
+
landing.html
|
|
74
197
|
`;
|