@apex-stack/core 0.6.0 → 0.7.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.
- package/dist/{build-XUDXRQ4W.js → build-NA6JEP3F.js} +1 -1
- package/dist/{chunk-DVNFDYEO.js → chunk-7CBGVRBB.js} +182 -158
- package/dist/chunk-P6KQSPAV.js +95 -0
- package/dist/cli.js +5 -5
- package/dist/{dev-74CABXDP.js → dev-6SH5IYNT.js} +1 -1
- package/dist/server-SSHQHMWY.js +479 -0
- package/dist/{start-TBG2TEOE.js → start-7HSIYO6C.js} +1 -1
- package/dist/{upgrade-GCRSV4IE.js → upgrade-KGUW3C3O.js} +1 -1
- package/package.json +1 -1
- package/templates/default/layouts/default.alpine +4 -2
- package/templates/default/pages/index.alpine +11 -6
- package/templates/default/public/favicon.svg +11 -0
- package/vscode/apex-alpine.vsix +0 -0
- package/vscode/version.txt +1 -0
- package/dist/chunk-CHBSGOB3.js +0 -42
- package/dist/server-VQSL2KPO.js +0 -321
|
@@ -89,164 +89,14 @@ async function resolveApexConfig(root, loadModule) {
|
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
// src/islands/render.ts
|
|
93
|
-
import { renderIslands } from "@apex-stack/kit";
|
|
94
|
-
var ISLAND_LOADER = (
|
|
95
|
-
/* js */
|
|
96
|
-
`
|
|
97
|
-
let __alpine
|
|
98
|
-
function __ensureAlpine() {
|
|
99
|
-
return __alpine ??= import('alpinejs').then(function (m) {
|
|
100
|
-
const Alpine = m.default
|
|
101
|
-
window.Alpine = Alpine
|
|
102
|
-
Alpine.start() // islands are x-ignore'd, so this hydrates nothing on its own
|
|
103
|
-
return Alpine
|
|
104
|
-
})
|
|
105
|
-
}
|
|
106
|
-
async function __hydrate(el) {
|
|
107
|
-
const Alpine = await __ensureAlpine()
|
|
108
|
-
// Global Alpine.start() marked this island with the internal _x_ignore
|
|
109
|
-
// property (from the x-ignore attribute). Clear BOTH so initTree will descend
|
|
110
|
-
// and initialize the island's own x-data instead of early-returning.
|
|
111
|
-
el.removeAttribute('x-ignore')
|
|
112
|
-
delete el._x_ignore
|
|
113
|
-
Alpine.initTree(el)
|
|
114
|
-
el.setAttribute('data-apex-hydrated', '')
|
|
115
|
-
}
|
|
116
|
-
document.querySelectorAll('[data-apex-island]').forEach(function (el) {
|
|
117
|
-
const mode = el.getAttribute('data-apex-client')
|
|
118
|
-
if (mode === 'load') {
|
|
119
|
-
__hydrate(el)
|
|
120
|
-
} else if (mode === 'idle') {
|
|
121
|
-
(window.requestIdleCallback || function (cb) { return setTimeout(cb, 200) })(function () { __hydrate(el) })
|
|
122
|
-
} else if (mode === 'visible') {
|
|
123
|
-
const io = new IntersectionObserver(function (entries, obs) {
|
|
124
|
-
entries.forEach(function (e) { if (e.isIntersecting) { obs.unobserve(e.target); __hydrate(e.target) } })
|
|
125
|
-
})
|
|
126
|
-
io.observe(el)
|
|
127
|
-
}
|
|
128
|
-
// 'none' \u2192 do nothing; the SSR HTML is the final, static output.
|
|
129
|
-
})
|
|
130
|
-
`.trim()
|
|
131
|
-
);
|
|
132
|
-
async function renderIslandsPage(opts) {
|
|
133
|
-
const mod = await opts.loadModule(opts.pageId);
|
|
134
|
-
const loaderData = await mod.loader({
|
|
135
|
-
params: opts.params ?? {},
|
|
136
|
-
url: opts.url,
|
|
137
|
-
config: opts.runtimeConfig ?? { public: {} },
|
|
138
|
-
locals: opts.locals ?? {}
|
|
139
|
-
}) ?? {};
|
|
140
|
-
const { html, hydratingCount } = renderIslands(
|
|
141
|
-
mod.template,
|
|
142
|
-
loaderData,
|
|
143
|
-
mod.scopeId,
|
|
144
|
-
opts.registry
|
|
145
|
-
);
|
|
146
|
-
const loaderScript = hydratingCount > 0 ? `
|
|
147
|
-
<script type="module">${ISLAND_LOADER}</script>` : "";
|
|
148
|
-
const configScript = hydratingCount > 0 ? `
|
|
149
|
-
${clientConfigScript(opts.publicConfig ?? {})}` : "";
|
|
150
|
-
const doc = `<!DOCTYPE html>
|
|
151
|
-
<html lang="en">
|
|
152
|
-
<head>
|
|
153
|
-
<meta charset="utf-8" />
|
|
154
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
155
|
-
<title>Apex JS \u2014 Islands</title>
|
|
156
|
-
<style>${mod.css}${opts.componentCss ?? ""}</style>
|
|
157
|
-
</head>
|
|
158
|
-
<body>
|
|
159
|
-
${html}${configScript}${loaderScript}
|
|
160
|
-
</body>
|
|
161
|
-
</html>`;
|
|
162
|
-
return opts.transformHtml ? opts.transformHtml(opts.url, doc) : doc;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// src/routing/router.ts
|
|
166
|
-
import { existsSync as existsSync2, readdirSync, statSync } from "fs";
|
|
167
|
-
import { join as join2, relative, sep } from "path";
|
|
168
|
-
function walkAlpine(dir) {
|
|
169
|
-
const out = [];
|
|
170
|
-
for (const entry of readdirSync(dir)) {
|
|
171
|
-
const abs = join2(dir, entry);
|
|
172
|
-
if (statSync(abs).isDirectory()) out.push(...walkAlpine(abs));
|
|
173
|
-
else if (entry.endsWith(".alpine")) out.push(abs);
|
|
174
|
-
}
|
|
175
|
-
return out;
|
|
176
|
-
}
|
|
177
|
-
function scanPages(root) {
|
|
178
|
-
const dir = join2(root, "pages");
|
|
179
|
-
if (!existsSync2(dir)) return [];
|
|
180
|
-
const routes = walkAlpine(dir).map((abs) => {
|
|
181
|
-
const rel = relative(dir, abs).split(sep).join("/");
|
|
182
|
-
const pageId = `/pages/${rel}`;
|
|
183
|
-
const parts = rel.replace(/\.alpine$/, "").split("/");
|
|
184
|
-
if (parts[parts.length - 1] === "index") parts.pop();
|
|
185
|
-
const segments = parts.map((p) => {
|
|
186
|
-
const catchAll = /^\[\.\.\.(.+)\]$/.exec(p);
|
|
187
|
-
if (catchAll) return { catchAll: catchAll[1] };
|
|
188
|
-
const m = /^\[(.+)\]$/.exec(p);
|
|
189
|
-
return m ? { param: m[1] } : { literal: p };
|
|
190
|
-
});
|
|
191
|
-
const isDynamic = segments.some((s) => s.param !== void 0 || s.catchAll !== void 0);
|
|
192
|
-
const pattern = `/${segments.map((s) => s.catchAll ? `:${s.catchAll}*` : s.param ? `:${s.param}` : s.literal).join("/")}`;
|
|
193
|
-
return { pageId, pattern, segments, isDynamic };
|
|
194
|
-
});
|
|
195
|
-
const rank = (r) => r.segments.some((s) => s.catchAll) ? 2 : r.isDynamic ? 1 : 0;
|
|
196
|
-
return routes.sort((a, b) => rank(a) - rank(b));
|
|
197
|
-
}
|
|
198
|
-
function pathSegments(url) {
|
|
199
|
-
const path = url.split("?")[0] ?? "/";
|
|
200
|
-
return path.split("/").filter(Boolean);
|
|
201
|
-
}
|
|
202
|
-
function matchRoute(routes, url) {
|
|
203
|
-
const segs = pathSegments(url);
|
|
204
|
-
for (const route of routes) {
|
|
205
|
-
const last = route.segments[route.segments.length - 1];
|
|
206
|
-
const isCatchAll = Boolean(last?.catchAll);
|
|
207
|
-
if (isCatchAll) {
|
|
208
|
-
const lead = route.segments.slice(0, -1);
|
|
209
|
-
if (segs.length < lead.length + 1) continue;
|
|
210
|
-
const params2 = {};
|
|
211
|
-
let ok2 = true;
|
|
212
|
-
for (let i = 0; i < lead.length; i++) {
|
|
213
|
-
const rs = lead[i];
|
|
214
|
-
const value = segs[i];
|
|
215
|
-
if (rs.param) params2[rs.param] = decodeURIComponent(value);
|
|
216
|
-
else if (rs.literal !== value) {
|
|
217
|
-
ok2 = false;
|
|
218
|
-
break;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
if (!ok2) continue;
|
|
222
|
-
params2[last?.catchAll] = segs.slice(lead.length).map(decodeURIComponent).join("/");
|
|
223
|
-
return { pageId: route.pageId, params: params2 };
|
|
224
|
-
}
|
|
225
|
-
if (route.segments.length !== segs.length) continue;
|
|
226
|
-
const params = {};
|
|
227
|
-
let ok = true;
|
|
228
|
-
for (let i = 0; i < route.segments.length; i++) {
|
|
229
|
-
const rs = route.segments[i];
|
|
230
|
-
const value = segs[i];
|
|
231
|
-
if (rs.param) params[rs.param] = decodeURIComponent(value);
|
|
232
|
-
else if (rs.literal !== value) {
|
|
233
|
-
ok = false;
|
|
234
|
-
break;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
if (ok) return { pageId: route.pageId, params };
|
|
238
|
-
}
|
|
239
|
-
return null;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
92
|
// src/stores/loader.ts
|
|
243
|
-
import { existsSync as
|
|
244
|
-
import { join as
|
|
93
|
+
import { existsSync as existsSync2, readdirSync } from "fs";
|
|
94
|
+
import { join as join2 } from "path";
|
|
245
95
|
async function loadStores(root, loadModule) {
|
|
246
|
-
const dir =
|
|
247
|
-
if (!
|
|
96
|
+
const dir = join2(root, "stores");
|
|
97
|
+
if (!existsSync2(dir)) return [];
|
|
248
98
|
const out = [];
|
|
249
|
-
for (const file of
|
|
99
|
+
for (const file of readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".js"))) {
|
|
250
100
|
const id = `/stores/${file}`;
|
|
251
101
|
const mod = await loadModule(id);
|
|
252
102
|
const def = mod.default;
|
|
@@ -387,6 +237,7 @@ ${storeRegs ? `${storeRegs}
|
|
|
387
237
|
<head>
|
|
388
238
|
<meta charset="utf-8" />
|
|
389
239
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
240
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
390
241
|
${headTags}
|
|
391
242
|
<style>${css}</style>
|
|
392
243
|
</head>
|
|
@@ -399,12 +250,185 @@ ${clientScript}
|
|
|
399
250
|
</html>`;
|
|
400
251
|
}
|
|
401
252
|
|
|
253
|
+
// src/islands/render.ts
|
|
254
|
+
import { renderIslands } from "@apex-stack/kit";
|
|
255
|
+
var SLOT_RE = /<slot\b[^>]*>[\s\S]*?<\/slot>/;
|
|
256
|
+
var ISLAND_LOADER = (
|
|
257
|
+
/* js */
|
|
258
|
+
`
|
|
259
|
+
let __alpine
|
|
260
|
+
function __ensureAlpine() {
|
|
261
|
+
return __alpine ??= import('alpinejs').then(function (m) {
|
|
262
|
+
const Alpine = m.default
|
|
263
|
+
window.Alpine = Alpine
|
|
264
|
+
Alpine.start() // islands are x-ignore'd, so this hydrates nothing on its own
|
|
265
|
+
return Alpine
|
|
266
|
+
})
|
|
267
|
+
}
|
|
268
|
+
async function __hydrate(el) {
|
|
269
|
+
const Alpine = await __ensureAlpine()
|
|
270
|
+
// Global Alpine.start() marked this island with the internal _x_ignore
|
|
271
|
+
// property (from the x-ignore attribute). Clear BOTH so initTree will descend
|
|
272
|
+
// and initialize the island's own x-data instead of early-returning.
|
|
273
|
+
el.removeAttribute('x-ignore')
|
|
274
|
+
delete el._x_ignore
|
|
275
|
+
Alpine.initTree(el)
|
|
276
|
+
el.setAttribute('data-apex-hydrated', '')
|
|
277
|
+
}
|
|
278
|
+
document.querySelectorAll('[data-apex-island]').forEach(function (el) {
|
|
279
|
+
const mode = el.getAttribute('data-apex-client')
|
|
280
|
+
if (mode === 'load') {
|
|
281
|
+
__hydrate(el)
|
|
282
|
+
} else if (mode === 'idle') {
|
|
283
|
+
(window.requestIdleCallback || function (cb) { return setTimeout(cb, 200) })(function () { __hydrate(el) })
|
|
284
|
+
} else if (mode === 'visible') {
|
|
285
|
+
const io = new IntersectionObserver(function (entries, obs) {
|
|
286
|
+
entries.forEach(function (e) { if (e.isIntersecting) { obs.unobserve(e.target); __hydrate(e.target) } })
|
|
287
|
+
})
|
|
288
|
+
io.observe(el)
|
|
289
|
+
}
|
|
290
|
+
// 'none' \u2192 do nothing; the SSR HTML is the final, static output.
|
|
291
|
+
})
|
|
292
|
+
`.trim()
|
|
293
|
+
);
|
|
294
|
+
async function renderIslandsPage(opts) {
|
|
295
|
+
const mod = await opts.loadModule(opts.pageId);
|
|
296
|
+
const cfg = opts.runtimeConfig ?? { public: {} };
|
|
297
|
+
const locals = opts.locals ?? {};
|
|
298
|
+
const loaderData = await mod.loader({
|
|
299
|
+
params: opts.params ?? {},
|
|
300
|
+
url: opts.url,
|
|
301
|
+
config: cfg,
|
|
302
|
+
locals
|
|
303
|
+
}) ?? {};
|
|
304
|
+
const head = mod.head ? await mod.head({
|
|
305
|
+
data: loaderData,
|
|
306
|
+
params: opts.params ?? {},
|
|
307
|
+
url: opts.url,
|
|
308
|
+
config: cfg,
|
|
309
|
+
locals
|
|
310
|
+
}) : void 0;
|
|
311
|
+
const authoredDefaults = mod.rootData ? mod.rootData() : {};
|
|
312
|
+
const data = { ...authoredDefaults, ...loaderData };
|
|
313
|
+
const available = opts.layouts ?? [];
|
|
314
|
+
const layoutName = mod.layout === false ? null : typeof mod.layout === "string" ? mod.layout : available.includes("default") ? "default" : null;
|
|
315
|
+
let template = mod.template;
|
|
316
|
+
let layoutCss = "";
|
|
317
|
+
const seen = /* @__PURE__ */ new Set();
|
|
318
|
+
let next = layoutName;
|
|
319
|
+
while (typeof next === "string" && available.includes(next) && !seen.has(next)) {
|
|
320
|
+
seen.add(next);
|
|
321
|
+
const layoutMod = await opts.loadModule(`/layouts/${next}.alpine`);
|
|
322
|
+
template = SLOT_RE.test(layoutMod.template) ? layoutMod.template.replace(SLOT_RE, () => template) : layoutMod.template + template;
|
|
323
|
+
layoutCss += layoutMod.css;
|
|
324
|
+
next = layoutMod.layout;
|
|
325
|
+
}
|
|
326
|
+
const { html, hydratingCount } = renderIslands(template, data, mod.scopeId, opts.registry);
|
|
327
|
+
const loaderScript = hydratingCount > 0 ? `
|
|
328
|
+
<script type="module">${ISLAND_LOADER}</script>` : "";
|
|
329
|
+
const configScript = hydratingCount > 0 ? `
|
|
330
|
+
${clientConfigScript(opts.publicConfig ?? {})}` : "";
|
|
331
|
+
const appCssLink = opts.appCss ? `
|
|
332
|
+
<link rel="stylesheet" href="${opts.appCss}" />` : "";
|
|
333
|
+
const doc = `<!DOCTYPE html>
|
|
334
|
+
<html lang="en">
|
|
335
|
+
<head>
|
|
336
|
+
<meta charset="utf-8" />
|
|
337
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
338
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
339
|
+
${renderHead(head)}${appCssLink}
|
|
340
|
+
<style>${mod.css}${layoutCss}${opts.componentCss ?? ""}</style>
|
|
341
|
+
</head>
|
|
342
|
+
<body>
|
|
343
|
+
${html}${configScript}${loaderScript}
|
|
344
|
+
</body>
|
|
345
|
+
</html>`;
|
|
346
|
+
return opts.transformHtml ? opts.transformHtml(opts.url, doc) : doc;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// src/routing/router.ts
|
|
350
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2, statSync } from "fs";
|
|
351
|
+
import { join as join3, relative, sep } from "path";
|
|
352
|
+
function walkAlpine(dir) {
|
|
353
|
+
const out = [];
|
|
354
|
+
for (const entry of readdirSync2(dir)) {
|
|
355
|
+
const abs = join3(dir, entry);
|
|
356
|
+
if (statSync(abs).isDirectory()) out.push(...walkAlpine(abs));
|
|
357
|
+
else if (entry.endsWith(".alpine")) out.push(abs);
|
|
358
|
+
}
|
|
359
|
+
return out;
|
|
360
|
+
}
|
|
361
|
+
function scanPages(root) {
|
|
362
|
+
const dir = join3(root, "pages");
|
|
363
|
+
if (!existsSync3(dir)) return [];
|
|
364
|
+
const routes = walkAlpine(dir).map((abs) => {
|
|
365
|
+
const rel = relative(dir, abs).split(sep).join("/");
|
|
366
|
+
const pageId = `/pages/${rel}`;
|
|
367
|
+
const parts = rel.replace(/\.alpine$/, "").split("/");
|
|
368
|
+
if (parts[parts.length - 1] === "index") parts.pop();
|
|
369
|
+
const segments = parts.map((p) => {
|
|
370
|
+
const catchAll = /^\[\.\.\.(.+)\]$/.exec(p);
|
|
371
|
+
if (catchAll) return { catchAll: catchAll[1] };
|
|
372
|
+
const m = /^\[(.+)\]$/.exec(p);
|
|
373
|
+
return m ? { param: m[1] } : { literal: p };
|
|
374
|
+
});
|
|
375
|
+
const isDynamic = segments.some((s) => s.param !== void 0 || s.catchAll !== void 0);
|
|
376
|
+
const pattern = `/${segments.map((s) => s.catchAll ? `:${s.catchAll}*` : s.param ? `:${s.param}` : s.literal).join("/")}`;
|
|
377
|
+
return { pageId, pattern, segments, isDynamic };
|
|
378
|
+
});
|
|
379
|
+
const rank = (r) => r.segments.some((s) => s.catchAll) ? 2 : r.isDynamic ? 1 : 0;
|
|
380
|
+
return routes.sort((a, b) => rank(a) - rank(b));
|
|
381
|
+
}
|
|
382
|
+
function pathSegments(url) {
|
|
383
|
+
const path = url.split("?")[0] ?? "/";
|
|
384
|
+
return path.split("/").filter(Boolean);
|
|
385
|
+
}
|
|
386
|
+
function matchRoute(routes, url) {
|
|
387
|
+
const segs = pathSegments(url);
|
|
388
|
+
for (const route of routes) {
|
|
389
|
+
const last = route.segments[route.segments.length - 1];
|
|
390
|
+
const isCatchAll = Boolean(last?.catchAll);
|
|
391
|
+
if (isCatchAll) {
|
|
392
|
+
const lead = route.segments.slice(0, -1);
|
|
393
|
+
if (segs.length < lead.length + 1) continue;
|
|
394
|
+
const params2 = {};
|
|
395
|
+
let ok2 = true;
|
|
396
|
+
for (let i = 0; i < lead.length; i++) {
|
|
397
|
+
const rs = lead[i];
|
|
398
|
+
const value = segs[i];
|
|
399
|
+
if (rs.param) params2[rs.param] = decodeURIComponent(value);
|
|
400
|
+
else if (rs.literal !== value) {
|
|
401
|
+
ok2 = false;
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (!ok2) continue;
|
|
406
|
+
params2[last?.catchAll] = segs.slice(lead.length).map(decodeURIComponent).join("/");
|
|
407
|
+
return { pageId: route.pageId, params: params2 };
|
|
408
|
+
}
|
|
409
|
+
if (route.segments.length !== segs.length) continue;
|
|
410
|
+
const params = {};
|
|
411
|
+
let ok = true;
|
|
412
|
+
for (let i = 0; i < route.segments.length; i++) {
|
|
413
|
+
const rs = route.segments[i];
|
|
414
|
+
const value = segs[i];
|
|
415
|
+
if (rs.param) params[rs.param] = decodeURIComponent(value);
|
|
416
|
+
else if (rs.literal !== value) {
|
|
417
|
+
ok = false;
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (ok) return { pageId: route.pageId, params };
|
|
422
|
+
}
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
|
|
402
426
|
export {
|
|
403
427
|
applyEnvToRuntimeConfig,
|
|
404
428
|
resolveApexConfig,
|
|
429
|
+
loadStores,
|
|
430
|
+
renderPage,
|
|
405
431
|
renderIslandsPage,
|
|
406
432
|
scanPages,
|
|
407
|
-
matchRoute
|
|
408
|
-
loadStores,
|
|
409
|
-
renderPage
|
|
433
|
+
matchRoute
|
|
410
434
|
};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// src/vscode.ts
|
|
2
|
+
import { spawnSync } from "child_process";
|
|
3
|
+
import { existsSync, readFileSync } from "fs";
|
|
4
|
+
import { createInterface } from "readline";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
var VSIX = fileURLToPath(new URL("../vscode/apex-alpine.vsix", import.meta.url));
|
|
7
|
+
var VERSION_FILE = fileURLToPath(new URL("../vscode/version.txt", import.meta.url));
|
|
8
|
+
var EXT_ID = "apex-stack.apex-alpine";
|
|
9
|
+
var WIN = process.platform === "win32";
|
|
10
|
+
var EDITOR_CLIS = ["code", "cursor", "windsurf", "codium"];
|
|
11
|
+
function extensionBundled() {
|
|
12
|
+
return existsSync(VSIX);
|
|
13
|
+
}
|
|
14
|
+
function bundledVersion() {
|
|
15
|
+
try {
|
|
16
|
+
const v = readFileSync(VERSION_FILE, "utf8").trim();
|
|
17
|
+
return v || null;
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function detectEditorCli() {
|
|
23
|
+
for (const cli of EDITOR_CLIS) {
|
|
24
|
+
try {
|
|
25
|
+
if (spawnSync(cli, ["--version"], { stdio: "ignore", shell: WIN }).status === 0) return cli;
|
|
26
|
+
} catch {
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
function installedVersion(cli) {
|
|
32
|
+
try {
|
|
33
|
+
const r = spawnSync(cli, ["--list-extensions", "--show-versions"], {
|
|
34
|
+
encoding: "utf8",
|
|
35
|
+
shell: WIN
|
|
36
|
+
});
|
|
37
|
+
if (r.status !== 0 || !r.stdout) return null;
|
|
38
|
+
const line = r.stdout.split("\n").find((l) => l.trim().toLowerCase().startsWith(`${EXT_ID}@`));
|
|
39
|
+
return line ? line.split("@")[1]?.trim() ?? null : null;
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function cmpVersion(a, b) {
|
|
45
|
+
const pa = a.split(".").map(Number);
|
|
46
|
+
const pb = b.split(".").map(Number);
|
|
47
|
+
for (let i = 0; i < 3; i++) {
|
|
48
|
+
if ((pa[i] || 0) !== (pb[i] || 0)) return (pa[i] || 0) - (pb[i] || 0);
|
|
49
|
+
}
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
function openInEditor(file, line = 1, col = 1) {
|
|
53
|
+
const cli = process.env.APEX_EDITOR || process.env.VISUAL || process.env.EDITOR || detectEditorCli();
|
|
54
|
+
if (!cli) return false;
|
|
55
|
+
try {
|
|
56
|
+
return spawnSync(cli, ["-g", `${file}:${line}:${col}`], { stdio: "ignore", shell: WIN }).status === 0;
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function installExtension(cli = detectEditorCli()) {
|
|
62
|
+
if (!cli || !existsSync(VSIX)) return false;
|
|
63
|
+
return spawnSync(cli, ["--install-extension", VSIX, "--force"], { stdio: "inherit", shell: WIN }).status === 0;
|
|
64
|
+
}
|
|
65
|
+
function promptYesNo(question, def = true) {
|
|
66
|
+
if (!process.stdin.isTTY) return Promise.resolve(def);
|
|
67
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
rl.question(`${question} ${def ? "(Y/n) " : "(y/N) "}`, (answer) => {
|
|
70
|
+
rl.close();
|
|
71
|
+
const a = answer.trim().toLowerCase();
|
|
72
|
+
resolve(a === "" ? def : a === "y" || a === "yes");
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async function offerExtension(choice) {
|
|
77
|
+
if (!extensionBundled()) return null;
|
|
78
|
+
const cli = detectEditorCli();
|
|
79
|
+
if (!cli) return null;
|
|
80
|
+
const bundled = bundledVersion();
|
|
81
|
+
const installed = installedVersion(cli);
|
|
82
|
+
if (installed && bundled && cmpVersion(installed, bundled) >= 0) {
|
|
83
|
+
return `.alpine extension up to date (${installed})`;
|
|
84
|
+
}
|
|
85
|
+
const prompt = installed && bundled ? `Update the Apex .alpine extension (${installed} \u2192 ${bundled})?` : "Install the Apex .alpine extension (syntax highlighting)?";
|
|
86
|
+
const yes = choice ?? await promptYesNo(prompt);
|
|
87
|
+
if (!yes) return null;
|
|
88
|
+
if (!installExtension(cli)) return "extension install failed";
|
|
89
|
+
return installed ? `.alpine extension updated to ${bundled}` : "Apex .alpine extension installed";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export {
|
|
93
|
+
openInEditor,
|
|
94
|
+
offerExtension
|
|
95
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
offerExtension
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-P6KQSPAV.js";
|
|
5
5
|
import {
|
|
6
6
|
VERSION,
|
|
7
7
|
banner,
|
|
@@ -147,13 +147,13 @@ var main = defineCommand2({
|
|
|
147
147
|
},
|
|
148
148
|
subCommands: {
|
|
149
149
|
new: newCommand,
|
|
150
|
-
dev: () => import("./dev-
|
|
151
|
-
build: () => import("./build-
|
|
152
|
-
start: () => import("./start-
|
|
150
|
+
dev: () => import("./dev-6SH5IYNT.js").then((m) => m.devCommand),
|
|
151
|
+
build: () => import("./build-NA6JEP3F.js").then((m) => m.buildCommand),
|
|
152
|
+
start: () => import("./start-7HSIYO6C.js").then((m) => m.startCommand),
|
|
153
153
|
make: () => import("./make-VAYO5GWA.js").then((m) => m.makeCommand),
|
|
154
154
|
add: () => import("./add-M3YLIFF5.js").then((m) => m.addCommand),
|
|
155
155
|
theme: () => import("./theme-UUOIV44V.js").then((m) => m.themeCommand),
|
|
156
|
-
upgrade: () => import("./upgrade-
|
|
156
|
+
upgrade: () => import("./upgrade-KGUW3C3O.js").then((m) => m.upgradeCommand),
|
|
157
157
|
migrate: () => import("./migrate-X6LIHMIE.js").then((m) => m.migrateCommand),
|
|
158
158
|
mcp: () => import("./mcp-CH7L4GF3.js").then((m) => m.mcpCommand)
|
|
159
159
|
},
|
|
@@ -24,7 +24,7 @@ var devCommand = defineCommand({
|
|
|
24
24
|
process.stdout.write(banner());
|
|
25
25
|
const sp = spinner(`Starting dev server${args.islands ? " (islands mode)" : ""}\u2026`);
|
|
26
26
|
try {
|
|
27
|
-
const { startDevServer } = await import("./server-
|
|
27
|
+
const { startDevServer } = await import("./server-SSHQHMWY.js");
|
|
28
28
|
const { port: actual } = await startDevServer({ root, port, islands: Boolean(args.islands) });
|
|
29
29
|
sp.succeed("Dev server ready");
|
|
30
30
|
ready([
|