mnfst-render 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.
@@ -5,9 +5,22 @@
5
5
  import { readFileSync, mkdirSync, writeFileSync, existsSync, rmSync, statSync, readdirSync, cpSync } from 'node:fs';
6
6
  import { join, resolve, dirname, relative, basename } from 'node:path';
7
7
  import { createServer } from 'node:http';
8
+ import { createRequire } from 'node:module';
8
9
  import { fileURLToPath } from 'node:url';
9
10
 
10
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
+ const require = createRequire(import.meta.url);
13
+
14
+ async function importFromProject(moduleName) {
15
+ // Ensure dependencies are resolved from the caller's project (cwd),
16
+ // not from this CLI package's own node_modules location.
17
+ try {
18
+ const resolved = require.resolve(moduleName, { paths: [process.cwd()] });
19
+ return await import(resolved);
20
+ } catch {
21
+ return await import(moduleName);
22
+ }
23
+ }
11
24
 
12
25
  // --- Config ------------------------------------------------------------------
13
26
 
@@ -773,8 +786,8 @@ async function runPrerender(config) {
773
786
 
774
787
  let browser;
775
788
  try {
776
- const chromium = await import('@sparticuz/chromium');
777
- const pptr = await import('puppeteer-core');
789
+ const chromium = await importFromProject('@sparticuz/chromium');
790
+ const pptr = await importFromProject('puppeteer-core');
778
791
  const executablePath = await chromium.default.executablePath();
779
792
  browser = await pptr.default.launch({
780
793
  args: chromium.default.args,
@@ -786,9 +799,12 @@ async function runPrerender(config) {
786
799
  } catch (serverlessErr) {
787
800
  let puppeteer;
788
801
  try {
789
- puppeteer = await import('puppeteer');
802
+ puppeteer = await importFromProject('puppeteer');
790
803
  } catch {
791
- console.error('prerender: install puppeteer (local) or puppeteer-core + @sparticuz/chromium.');
804
+ console.error('prerender: missing browser runtime.');
805
+ console.error('Install one of the following, then rerun:');
806
+ console.error(' npm i -D puppeteer');
807
+ console.error(' npm i -D puppeteer-core @sparticuz/chromium');
792
808
  process.exit(1);
793
809
  }
794
810
  browser = await puppeteer.default.launch({ headless: true });
@@ -930,6 +946,50 @@ async function runPrerender(config) {
930
946
  });
931
947
  });
932
948
 
949
+ // Remove orphan x-for clones that still reference loop-scope vars (e.g. image/index)
950
+ // outside their template scope. These throw Alpine errors in live static hosting.
951
+ await page.evaluate(() => {
952
+ const loopVarRegex = /^\s*(?:\(\s*([A-Za-z_$][\w$]*)(?:\s*,\s*([A-Za-z_$][\w$]*))?\s*\)|([A-Za-z_$][\w$]*))\s+in\s+/;
953
+ const bindingAttrRegex = /^(?:x-bind:|:|x-text|x-html|x-show|x-if|x-model|x-effect|x-on:|@)/;
954
+
955
+ document.querySelectorAll('template[x-for]').forEach((tpl) => {
956
+ const xFor = (tpl.getAttribute('x-for') || '').trim();
957
+ const m = xFor.match(loopVarRegex);
958
+ const itemVar = m ? (m[1] || m[3] || '') : '';
959
+ const indexVar = m ? (m[2] || '') : '';
960
+ if (!itemVar && !indexVar) return;
961
+
962
+ const first = tpl.content?.firstElementChild;
963
+ if (!first) return;
964
+ const tag = first.tagName;
965
+
966
+ let next = tpl.nextElementSibling;
967
+ while (next) {
968
+ const sameTag = next.tagName === tag;
969
+ if (!sameTag) break;
970
+
971
+ let referencesLoopScope = false;
972
+ const attrNodes = next.attributes ? Array.from(next.attributes) : [];
973
+ for (const attr of attrNodes) {
974
+ if (!bindingAttrRegex.test(attr.name)) continue;
975
+ const expr = attr.value || '';
976
+ if (
977
+ (itemVar && new RegExp(`\\b${itemVar}\\b`).test(expr)) ||
978
+ (indexVar && new RegExp(`\\b${indexVar}\\b`).test(expr))
979
+ ) {
980
+ referencesLoopScope = true;
981
+ break;
982
+ }
983
+ }
984
+
985
+ const toRemove = next;
986
+ next = next.nextElementSibling;
987
+ if (referencesLoopScope) toRemove.remove();
988
+ else break;
989
+ }
990
+ });
991
+ });
992
+
933
993
  // Remove elements marked data-dynamic (so they are not in static HTML; client will render them).
934
994
  // Skip <template> since we only collapse those above; other elements and their subtree are removed.
935
995
  await page.evaluate(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mnfst-render",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Render Manifest sites to static HTML for SEO",
5
5
  "type": "module",
6
6
  "bin": {