@litodocs/cli 1.2.0 → 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/package.json +1 -1
- package/src/core/landing-sync.js +128 -15
package/package.json
CHANGED
package/src/core/landing-sync.js
CHANGED
|
@@ -13,6 +13,46 @@ const { copy, ensureDir, readFile, writeFile, pathExists, readJson } = pkg;
|
|
|
13
13
|
import { join, relative, basename, extname } from 'path';
|
|
14
14
|
import { readdir } from 'fs/promises';
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Add is:inline to all <script> and <style> tags in HTML so Astro ships
|
|
18
|
+
* them as-is. Without this, Astro treats scripts as ES modules (scoping
|
|
19
|
+
* declarations, breaking onclick handlers) and scopes styles (breaking
|
|
20
|
+
* global CSS like :root variables, animations, etc.).
|
|
21
|
+
*/
|
|
22
|
+
function inlineForAstro(html) {
|
|
23
|
+
// Add is:inline to <script> tags that don't already have it
|
|
24
|
+
html = html.replace(/<script(?![^>]*is:inline)([^>]*>)/gi, '<script is:inline$1');
|
|
25
|
+
// Add is:inline to <style> tags that don't already have is:inline or is:global
|
|
26
|
+
html = html.replace(/<style(?![^>]*is:(?:inline|global))([^>]*>)/gi, '<style is:inline$1');
|
|
27
|
+
return html;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check if HTML is a full document (has <html> or <!doctype>).
|
|
32
|
+
* If so, extract head content, body content, and html/body attributes
|
|
33
|
+
* so we can merge them into the Astro template properly.
|
|
34
|
+
*/
|
|
35
|
+
function parseFullHtmlDocument(html) {
|
|
36
|
+
const isFullDoc = /<!doctype\s+html|<html[\s>]/i.test(html);
|
|
37
|
+
if (!isFullDoc) return null;
|
|
38
|
+
|
|
39
|
+
// Extract <html> tag attributes
|
|
40
|
+
const htmlTagMatch = html.match(/<html([^>]*)>/i);
|
|
41
|
+
const htmlAttrs = htmlTagMatch ? htmlTagMatch[1].trim() : '';
|
|
42
|
+
|
|
43
|
+
// Extract <head> inner content
|
|
44
|
+
const headMatch = html.match(/<head[^>]*>([\s\S]*)<\/head>/i);
|
|
45
|
+
const headContent = headMatch ? headMatch[1].trim() : '';
|
|
46
|
+
|
|
47
|
+
// Extract <body> tag attributes and inner content
|
|
48
|
+
const bodyTagMatch = html.match(/<body([^>]*)>/i);
|
|
49
|
+
const bodyAttrs = bodyTagMatch ? bodyTagMatch[1].trim() : '';
|
|
50
|
+
const bodyMatch = html.match(/<body[^>]*>([\s\S]*)<\/body>/i);
|
|
51
|
+
const bodyContent = bodyMatch ? bodyMatch[1].trim() : '';
|
|
52
|
+
|
|
53
|
+
return { htmlAttrs, headContent, bodyContent, bodyAttrs };
|
|
54
|
+
}
|
|
55
|
+
|
|
16
56
|
/**
|
|
17
57
|
* Landing page types
|
|
18
58
|
*/
|
|
@@ -309,6 +349,9 @@ async function generateAstroLanding(projectDir, landingData) {
|
|
|
309
349
|
|
|
310
350
|
let htmlContent = await readFile(join(sourcePath, mainHtml), 'utf-8');
|
|
311
351
|
|
|
352
|
+
// Make all <script> tags in the user's HTML pass through Astro untouched
|
|
353
|
+
htmlContent = inlineForAstro(htmlContent);
|
|
354
|
+
|
|
312
355
|
// Read CSS files and write to a separate file
|
|
313
356
|
let cssContent = '';
|
|
314
357
|
for (const cssFile of cssFiles) {
|
|
@@ -327,7 +370,36 @@ async function generateAstroLanding(projectDir, landingData) {
|
|
|
327
370
|
jsContent += `// ${jsFile}\n${js}\n\n`;
|
|
328
371
|
}
|
|
329
372
|
|
|
330
|
-
//
|
|
373
|
+
// Check if the user's HTML is a full document (has <html>, <head>, <body>)
|
|
374
|
+
const parsed = parseFullHtmlDocument(htmlContent);
|
|
375
|
+
|
|
376
|
+
let astroContent;
|
|
377
|
+
|
|
378
|
+
if (parsed) {
|
|
379
|
+
// Full HTML document: merge the user's head/body into the Astro page
|
|
380
|
+
// instead of nesting an entire HTML document inside another one.
|
|
381
|
+
astroContent = generateAstroFromFullDoc(parsed, { cssFiles, jsContent, navbarContent, footerContent });
|
|
382
|
+
} else {
|
|
383
|
+
// HTML fragment: wrap it in a full Astro page
|
|
384
|
+
astroContent = generateAstroFromFragment(htmlContent, { jsContent, navbarContent, footerContent });
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Write to index.astro
|
|
388
|
+
const indexPath = join(projectDir, 'src', 'pages', 'index.astro');
|
|
389
|
+
await writeFile(indexPath, astroContent, 'utf-8');
|
|
390
|
+
|
|
391
|
+
// Copy assets if they exist
|
|
392
|
+
await copyLandingAssets(sourcePath, projectDir);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Generate Astro page from a full HTML document.
|
|
397
|
+
* Extracts <head> and <body> content, preserves the user's structure.
|
|
398
|
+
*/
|
|
399
|
+
function generateAstroFromFullDoc(parsed, { cssFiles, jsContent, navbarContent, footerContent }) {
|
|
400
|
+
const { htmlAttrs, headContent, bodyContent, bodyAttrs } = parsed;
|
|
401
|
+
|
|
402
|
+
// Determine header/footer rendering
|
|
331
403
|
const navbarIsHidden = navbarContent === '__hidden__';
|
|
332
404
|
const footerIsHidden = footerContent === '__hidden__';
|
|
333
405
|
const hasCustomNavbar = !navbarIsHidden && !!navbarContent;
|
|
@@ -338,16 +410,64 @@ async function generateAstroLanding(projectDir, landingData) {
|
|
|
338
410
|
const headerRender = navbarIsHidden
|
|
339
411
|
? ''
|
|
340
412
|
: hasCustomNavbar
|
|
341
|
-
? `<header class="landing-custom-navbar">\n ${navbarContent}\n </header>`
|
|
413
|
+
? `<header class="landing-custom-navbar">\n ${inlineForAstro(navbarContent)}\n </header>`
|
|
342
414
|
: '<Header />';
|
|
343
415
|
const footerRender = footerIsHidden
|
|
344
416
|
? ''
|
|
345
417
|
: hasCustomFooter
|
|
346
|
-
? `<footer class="landing-custom-footer">\n ${footerContent}\n </footer>`
|
|
418
|
+
? `<footer class="landing-custom-footer">\n ${inlineForAstro(footerContent)}\n </footer>`
|
|
347
419
|
: '<Footer />';
|
|
348
420
|
|
|
349
|
-
|
|
350
|
-
|
|
421
|
+
return `---
|
|
422
|
+
// Custom landing page - auto-generated by Lito CLI
|
|
423
|
+
// Source: _landing/ folder (full HTML document)
|
|
424
|
+
import '../styles/landing.css';
|
|
425
|
+
${headerImport}
|
|
426
|
+
${footerImport}
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
<!doctype html>
|
|
430
|
+
<html ${htmlAttrs}>
|
|
431
|
+
<head>
|
|
432
|
+
${headContent}
|
|
433
|
+
</head>
|
|
434
|
+
<body ${bodyAttrs}>
|
|
435
|
+
${headerRender}
|
|
436
|
+
|
|
437
|
+
${bodyContent}
|
|
438
|
+
|
|
439
|
+
${footerRender}
|
|
440
|
+
|
|
441
|
+
${jsContent ? `<script is:inline>\n${jsContent}\n</script>` : ''}
|
|
442
|
+
</body>
|
|
443
|
+
</html>
|
|
444
|
+
`;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Generate Astro page from an HTML fragment.
|
|
449
|
+
* Wraps it in a full Astro page with Lito's defaults.
|
|
450
|
+
*/
|
|
451
|
+
function generateAstroFromFragment(htmlContent, { jsContent, navbarContent, footerContent }) {
|
|
452
|
+
const navbarIsHidden = navbarContent === '__hidden__';
|
|
453
|
+
const footerIsHidden = footerContent === '__hidden__';
|
|
454
|
+
const hasCustomNavbar = !navbarIsHidden && !!navbarContent;
|
|
455
|
+
const hasCustomFooter = !footerIsHidden && !!footerContent;
|
|
456
|
+
|
|
457
|
+
const headerImport = navbarIsHidden || hasCustomNavbar ? '' : "import Header from '../components/Header.astro';";
|
|
458
|
+
const footerImport = footerIsHidden || hasCustomFooter ? '' : "import Footer from '../components/Footer.astro';";
|
|
459
|
+
const headerRender = navbarIsHidden
|
|
460
|
+
? ''
|
|
461
|
+
: hasCustomNavbar
|
|
462
|
+
? `<header class="landing-custom-navbar">\n ${inlineForAstro(navbarContent)}\n </header>`
|
|
463
|
+
: '<Header />';
|
|
464
|
+
const footerRender = footerIsHidden
|
|
465
|
+
? ''
|
|
466
|
+
: hasCustomFooter
|
|
467
|
+
? `<footer class="landing-custom-footer">\n ${inlineForAstro(footerContent)}\n </footer>`
|
|
468
|
+
: '<Footer />';
|
|
469
|
+
|
|
470
|
+
return `---
|
|
351
471
|
// Custom landing page - auto-generated by Lito CLI
|
|
352
472
|
// Source: _landing/ folder
|
|
353
473
|
import '../styles/global.css';
|
|
@@ -388,17 +508,10 @@ const config = await getConfigFile();
|
|
|
388
508
|
|
|
389
509
|
${footerRender}
|
|
390
510
|
|
|
391
|
-
${jsContent ? `<script>\n${jsContent}\n</script>` : ''}
|
|
511
|
+
${jsContent ? `<script is:inline>\n${jsContent}\n</script>` : ''}
|
|
392
512
|
</body>
|
|
393
513
|
</html>
|
|
394
514
|
`;
|
|
395
|
-
|
|
396
|
-
// Write to index.astro
|
|
397
|
-
const indexPath = join(projectDir, 'src', 'pages', 'index.astro');
|
|
398
|
-
await writeFile(indexPath, astroContent, 'utf-8');
|
|
399
|
-
|
|
400
|
-
// Copy assets if they exist
|
|
401
|
-
await copyLandingAssets(sourcePath, projectDir);
|
|
402
515
|
}
|
|
403
516
|
|
|
404
517
|
/**
|
|
@@ -708,12 +821,12 @@ async function generateAstroSectionsLanding(projectDir, landingData) {
|
|
|
708
821
|
const headerRender = navbarIsHidden
|
|
709
822
|
? ''
|
|
710
823
|
: hasCustomNavbar
|
|
711
|
-
? `<header class="landing-custom-navbar">\n ${navbarContent}\n </header>`
|
|
824
|
+
? `<header class="landing-custom-navbar">\n ${inlineForAstro(navbarContent)}\n </header>`
|
|
712
825
|
: '<Header />';
|
|
713
826
|
const footerRender = footerIsHidden
|
|
714
827
|
? ''
|
|
715
828
|
: hasCustomFooter
|
|
716
|
-
? `<footer class="landing-custom-footer">\n ${footerContent}\n </footer>`
|
|
829
|
+
? `<footer class="landing-custom-footer">\n ${inlineForAstro(footerContent)}\n </footer>`
|
|
717
830
|
: '<Footer />';
|
|
718
831
|
|
|
719
832
|
const astroContent = `---
|